Acknowledgement

These materials are adapted from a course developed at Cancer Research Uk Cambridge Institute by Mark Dunning, Matthew Eldridge and Thomas Carroll.

R basics

Advantages of R

The R programming language is now recognised beyond the academic community as an effect solution for data analysis and visualisation. Notable users of R include:-

Key features

  • Open-source
  • Cross-platform
  • Access to existing visualisation / statistical tools
  • Flexibility
  • Visualisation and interactivity
  • Add-ons for many fields of research
  • Facilitating Reproducible Research
duke-scandal

duke-scandal

Two Biostatiscians (later termed ‘Forensic Bioinformaticians’) from M.D. Anderson used R extensively during their re-analysis and investigation of a Clinical Prognostication paper from Duke. The subsequent scandal put Reproducible Research at the forefront of everyone’s mind.

Keith Baggerly’s talk on the subject is highy-recommended.

Support for R

  • Online forums such as Stack Overflow regularly feature R
  • Blogs
  • Local user groups
  • e.g. Sheffield-R
  • Documentation via ? or help.start()
  • Documentation for packages is found via the Packages tab in the bottom-right of RStudio.
  • Packages analyse all kinds of Genomic data (>800)
  • Compulsory documentation (vignettes) for each package
  • 6-month release cycle
  • Course Materials
  • Example data and workflows
  • Common, re-usable framework and functionality
  • Available Support
    • Often you will be able to interact with the package maintainers / developers and other power-users of the project software

RStudio

  • Rstudio is a free environment for R
  • Convenient menus to access scripts, display plots
  • Still need to use command-line to get things done
  • Developed by some of the leading R programmers
  • Used by beginners, and experienced users alike

To get started, you will need to install the latest version of R and RStudio Desktop; both of which are free.

Once installed, you should be able to launch RStudio by clicking on its icon:-

Entering commands in R

  • The traditional way to enter R commands is via the Terminal, or using the console in RStudio (bottom-left panel when RStudio opens for first time).
  • this doesn’t automatically keep track of the steps you did
  • Alternative, an R script can be used to keep a record of the commands you used.
  • The R code can be run from inside the script and the results are displayed in the console or Viewer panel (eg plots)
  • Each line of R code can be executed by clicking on the line and pressing CTRL and ENTER
  • Try this now!
print("Hello World")
[1] "Hello World"
  • You can add R chunks by pressing CRTL + ALT + I
    • or using the Insert menu option
    • (can also include code from other languages such as Python or bash)
    • try and avoid adding code chunks manually

Getting started

At a basic level, we can use R as a calculator to compute simple sums with the +, -, * (for multiplication) and / (for division) symbols.

2 + 2
[1] 4
2 - 2
[1] 0
4 * 3
[1] 12
10 / 2
[1] 5

The answer is displayed at the console with a [1] in front of it. The 1 inside the square brackets is a place-holder to signify how many values were in the answer (in this case only one). We will talk about dealing with lists of numbers shortly…

In the case of expressions involving multiple operations, R respects the BODMAS system to decide the order in which operations should be performed.

2 + 2 *3
[1] 8
2 + (2 * 3)
[1] 8
(2 + 2) * 3
[1] 12

R is capable of more complicated arithmetic such as trigonometry and logarithms; like you would find on a fancy scientific calculator. Of course, R also has a plethora of statistical operations as we will see.

pi
[1] 3.141593
sin (pi/2)
[1] 1
cos(pi)
[1] -1
tan(2)
[1] -2.18504
log(1)
[1] 0

We can only go so far with performing simple calculations like this. Eventually we will need to store our results for later use. For this, we need to make use of variables.

Variables

A variable is a letter or word which takes (or contains) a value. We use the assignment ‘operator’, <- to create a variable and store some value in it.

x <- 10
x
[1] 10
myNumber <- 25
myNumber
[1] 25

We also can perform arithmetic on variables using functions:

sqrt(myNumber)
[1] 5

We can add variables together:

x + myNumber
[1] 35

We can change the value of an existing variable:

x <- 21
x
[1] 21
  • We can set one variable to equal the value of another variable:
x <- myNumber
x
[1] 25
  • We can modify the contents of a variable:
myNumber <- myNumber + sqrt(16)
myNumber
[1] 29

When we are feeling lazy we might give our variables short names (x, y, i…etc), but a better practice would be to give them meaningful names. There are some restrictions on creating variable names. They cannot start with a number or contain characters such as ., _, ‘-’. Naming variables the same as in-built functions in R, such as c, T, mean should also be avoided.

Naming variables is a matter of taste. Some conventions exist such as a separating words with - or using camelCaps. Whatever convention you decided, stick with it!

Functions

Functions in R perform operations on arguments (the inputs(s) to the function). We have already used:

sin(x)
[1] -0.1323518

this returns the sine of x. In this case the function has one argument: x. Arguments are always contained in parentheses – curved brackets, () – separated by commas.

Arguments can be named or unnamed, but if they are unnamed they must be ordered (we will see later how to find the right order). The names of the arguments are determined by the author of the function and can be found in the help page for the function. When testing code, it is easier and safer to name the arguments. seq is a function for generating a numeric sequence from and to particular numbers. Type ?seq to get the help page for this function.

seq(from = 3, to = 20, by = 4)
[1]  3  7 11 15 19
seq(3, 20, 4)
[1]  3  7 11 15 19

Arguments can have default values, meaning we do not need to specify values for these in order to run the function.

rnorm is a function that will generate a series of values from a normal distribution. In order to use the function, we need to tell R how many values we want

## this will produce a random set of numbers, so everyone will get a different set of numbers
rnorm(n=10)
 [1]  0.53276152  0.18069520  0.32521785 -1.19523658  0.84161373 -1.23798208 -0.63446815  0.02678897 -0.41640561 -0.01388716

The normal distribution is defined by a mean (average) and standard deviation (spread). However, in the above example we didn’t tell R what mean and standard deviation we wanted. So how does R know what to do? All arguments to a function and their default values are listed in the help page

(N.B sometimes help pages can describe more than one function)

?rnorm

In this case, we see that the defaults for mean and standard deviation are 0 and 1. We can change the function to generate values from a distribution with a different mean and standard deviation using the mean and sd arguments. It is important that we get the spelling of these arguments exactly right, otherwise R will an error message, or (worse?) do something unexpected.

rnorm(n=10, mean=2,sd=3)
 [1]  1.7982960  2.4871747 -0.4191346  3.6750166  9.2721932 -0.9166252  3.8040163  5.8764153  2.3982508  1.7998527
rnorm(10, 2, 3)
 [1] 6.6136382 0.7971393 0.4677969 5.6532190 5.2741323 1.0236882 3.8722071 3.5039064 7.0018721 5.4728296

In the examples above, seq and rnorm were both outputting a series of numbers, which is called a vector in R and is the most-fundamental data-type.




Exercise

  • What is the value of pi to 3 decimal places?
    • see the help for round ?round
  • How can we a create a sequence from 2 to 20 comprised of 5 equally-spaced numbers?
    • check the help page for seq ?seq
  • Create a variable containing 1000 random numbers with a mean of 2 and a standard deviation of 3
    • what is the maximum and minimum of these numbers?
    • what is the average?
    • HINT: see the help pages for functions min, max and mean



Packages in R

So far we have used functions that are available with the base distribution of R; the functions you get with a clean install of R. The open-source nature of R encourages others to write their own functions for their particular data-type or analyses.

Packages are distributed through repositories. The most-common ones are CRAN and Bioconductor. CRAN alone has many thousands of packages.

The Packages tab in the bottom-right panel of RStudio lists all packages that you currently have installed. Clicking on a package name will show a list of functions that available once that package has been loaded.

There are functions for installing packages within R. If your package is part of the main CRAN repository, you can use install.packages

We will be using the tidyverse R package in this practical. To install it, we would do.

install.packages("tidyverse")

A package may have several dependencies; other R packages from which it uses functions or data types (re-using code from other packages is strongly-encouraged). If this is the case, the other R packages will be located and installed too.

So long as you stick with the same version of R, you won’t need to repeat this install process.

Once a package is installed, the library function is used to load a package and make it’s functions / data available in your current R session. You need to do this every time you load a new RStudio session. Let’s go ahead and load the tidyverse.

## tidyverse is a collection of packages for data manipulation and visualisation
library(tidyverse)

Dealing with data

The tidyverse is in fact an eco-system of packages that provides a consistent, intuitive system for data manipulation and visualisation in R.

Image Credit: Aberdeen Study Group

We are going to explore some of the basic features of the tidyverse using data from the gapminder project, which have been bundled into an R package. These data give various indicator variables for different countries around the world (life expectancy, population and Gross Domestic Product). We have saved these data as a .csv file called gapmider.csv in a sub-directory called raw_data/ to demonstrate how to import data into R.

You can download these data, along with the rest of the material needed for today’s workshop and a copy of this handout here. Save the .zip file somewhere on your computer and unzip it.

Working in Rstudio Projects

We are also going to working in an Rstudio Project. We suggest you organize each data analysis into a project: a folder on your computer containing all files relevant to a particular piece of work. There are a number of benefits to this practice in general, and the Rstudio implementation in particular, summarised in Jenny Bryan’s blogpost on Project-oriented workflows.

Makes work:

  • Self-contained
  • Portable

RStudio fully supports Project-based workflows, making it easy to switch from one to another, have many projects open at once, re-launch recently used Projects, etc.

There are a few ways to create new Projects. We can start with a new Project in a nee directory but we can also turn an existing directory into a Project. Let’s do this with the unzipped folder containing the workshop materials we just downloaded.

File > New Project > Existing Directory > …

choose workshop material directory

We’ve now turned our workshop material folder into an Rstudio project and launched it, which means:

  • a fresh R session has been launched (see how the Environment tab is now clear)
  • the working directory has been set to the project root (you can check this form the header on the Console tab)
  • A project specific History is initiated.

Working in R Notebooks

We’ll also be working in an R Notebook. These file are an R Markdown document type, which combine R code with markdown, a documentation language, providing a framework for literate programming. In an R Notebook, R code chunks can be executed independently and interactively, with output visible immediately beneath the input.

Let’s open an R Notebook to start work in.

File > New File > R Notebook

  • give it a title
  • save it

Each chunk of R code looks something like this.

```{r foo}
print('hello world!')
```

New R code chunks can be inserted by:

  • keyboard shortcut: Ctrl + Alt + I (macOS: Cmd + Option + I)
  • Insert menu in the editor toolbar.

Each line of R can be again executed by clicking on the line and pressing Ctrl + Enter (macOS: Cmd + Enter), or you can press the green triangle on the right-hand side to run everything in the chunk.

Specifying good file paths

One of the biggest strengths of working in Projects is the convention of specifying file paths relative to the project root. If all file paths in the project are specified relative to this root, code involving paths will direct to the correct locations, even if the Project is moved.

A great way to create robust file paths is by using function here() in package here. The function constructs paths to your project’s files relative to a project’s root that work across different operating systems.

Let’s first install the package:

install.packages("here")

We can now use function here() to create a path to the gapminder.csv in the raw_data/ folder. We can assign it to a variable we’ll use next to load the file.

gapminder_path <- here::here("raw_data", "gapminder.csv")
gapminder_path
[1] "/Users/Anna/Documents/workflows/workshops/r-crash-course/raw_data/gapminder.csv"

Question: Why would specifying gapminder_path as "/Users/Anna/Documents/workflows/workshops/r-crash-course/raw_data/gapminder.csv" be a bad idea?

Reading in data

Any .csv file can be imported into R by supplying the path to the file to readr function read_csv and assigning it to a new object to store the result. A useful sanity check is the file.exists function which will print TRUE is the file can be found in the working directory.

file.exists(gapminder_path)
[1] TRUE

Assuming the file can be found, we can use read_csv to import. Other functions can be used to read tab-delimited files (read_delim) or a generic read.table function. A data frame object is created.

gapminder <- read_csv(gapminder_path)
Parsed with column specification:
cols(
  country = col_character(),
  continent = col_character(),
  year = col_integer(),
  lifeExp = col_double(),
  pop = col_integer(),
  gdpPercap = col_double()
)

The data frame object in R allows us to work with “tabular” data, like we might be used to dealing with in Excel, where our data can be thought of having rows and columns. The values in each column have to all be of the same type (i.e. all numbers or all text).

In Rstudio, you can view the contents of the data frame we have just created. This is useful for interactive exploration of the data, but not so useful for automation and scripting and analyses.

View(gapminder)

We should always check the data frame that we have created. Sometimes R will happily read data using an inappropriate function and create an object without raising an error. However, the data might be unsuable. Consider:-

test <- read_table(gapminder_path)
Parsed with column specification:
cols(
  `"country","continent","year","lifeExp","pop","gdpPercap"` = col_character()
)
View(test)

Manipulating data

We are going to use functions from the dplyr package, which is automatically loaded by loading the tidyverse to manipulate the data frame we have just created. It is perfectly possible to work with data frame using the functions provided as part of “base R”. However, many find it easy to read and write code using dplyr.

There are many more functions available in dplyr than we will cover today. An overview of all functions is given in the following cheatsheet

selecting columns

We can access the columns of a data frame by knowing the column name using the select function. The column names that we want to display are listed after the name of the data frame, separated by a , .

select(gapminder, country,continent)

We can also suppress some columns from appearing in the output by putting a - in front of the column name.

select(gapminder, -country)

A range of columns can be selected by the : operator.

select(gapminder, lifeExp:gdpPercap)

There are a number of helper functions can be employed if we are unsure about the exact name of the column.

select(gapminder, starts_with("life"))
select(gapminder, contains("pop"))

Restricting rows with filter

So far we have been returning all the rows in the output. We can use what we call a logical test to define what rows are displayed in the output. This is a test that gives either a TRUE or FALSE result. When applied to subsetting, only rows with a TRUE result get returned.

For example we could compare the lifeExp variable to 40. Internally, R creates a vector of TRUE or FALSE; one for each row in the data frame. This is then used to decide which rows to display.

filter(gapminder, lifeExp < 40)

Testing for equality can be done using ==. This will only give TRUE for entries that are exactly the same as the test string.

filter(gapminder, country == "Zambia")

N.B. For partial matches, the grep function and / or regular expressions (if you know them) can be used.

filter(gapminder, grepl("land", country))

There are a couple of ways of testing for more than one text value. The first uses an or | statement. i.e. testing if the value of country is Zambia or the value is Zimbabwe. Remember to use double = sign to test for string equality; ==.

The %in% function is a convenient function for testing which items in a vector correspond to a defined set of values.

We can require that both tests are TRUE by using an and & operation. e.g. which years in Zambia had a life expectancy less than 40.

filter(gapminder, country == "Zambia" & lifeExp < 40)
filter(gapminder, country == "Zambia", lifeExp < 40)

To allow

filter(gapminder, country == "Zambia" | country == "Zimbabwe")

Finally, we have != for testing if something is not equal

filter(gapminder, continent != "Europe")



Exercise

  • Create a subset of the data where the population less than a million in the year 2002
  • Create a subset of the data where the life expectancy is greater than 80 in the year 2002
  • Create a subset of the European data where the life expectancy is greater than 80 in either the year 2002 or 2007



As well as selecting existing columns in the data frame, new columns created using the mutate function. We would typically use a function that would take an existing column and apply some operation to each entry in the column in-turn. In other words, the number of values returned by the function must be the same as the number of input values.

gapminder_extra <- mutate(gapminder, PopInMillions = pop / 1e6)
gapminder_extra <- mutate(gapminder_extra, lifeExp = round(lifeExp))
gapminder_extra

Ordering and sorting

The whole data frame can be re-ordered according to the values in one column using the arrange function. So to order the table according to population size:-

arrange(gapminder,pop)
NA

The default is smallest --> largest by we can change this using the desc function

arrange(gapminder,desc(pop))

arrange also works on character vectors

arrange(gapminder, desc(country))

We can even order by more than one condition

arrange(gapminder, year,pop)

A final point on data frames is that we can export them out of R once we have done our data processing.

Let’s create a folder in which to store such processed, analysis ready data

dir.create("data")
byWealth <- arrange(gapminder, desc(gdpPercap))
head(byWealth)
write_csv(byWealth, path = here::here("data", "dataOrderedByWealth.csv"))

We will now try an exercise that involves using several steps of these operations




Exercise

  • Filter the data to include just observations from the year 2002
  • Order the table by increasing life expectancy
  • Remove the year column from the resulting data frame
  • Write the data frame out to a file



“Piping”

We will often need to perform an analysis, or clean a dataset, using several dplyr functions in sequence. e.g. filtering, mutating, then selecting columns of interest (possibly followed by plotting - see later).

If we wanted to filter our results to just Europe there is no point displaying the continent column in our output, so we don’t need to show it.

The following is perfectly valid R code, but invites the user to make mistakes when writing it. We also have to create multiple copies of the same data frame.

tmp <- filter(gapminder, continent == "Europe")
tmp2 <- select(tmp, -continent)

Those familiar with Unix may recall that commands can be joined with a pipe; |

In R, dplyr commands to be linked together and form a workflow. The symbol %>% is pronounced then. With a %>% the input to a function is assumed to be the output of the previous line. All the dplyr functions that we have seen so far take a data frame as an input and return an altered data frame as an output, so are ameanable to this type of programming.

The example we gave of filtering just the European countries and removing the continent column becomes:-

notice that in the select statement we don’t need to specify the name of the data frame

filter(gapminder, continent=="Europe") %>% 
  select(-continent)

We can join as many dplyr functions as we require for the analysis.

filter(gapminder, continent=="Europe") %>% 
  select(-continent) %>% 
  mutate(lifeExp = round(lifeExp)) %>% 
  arrange(year,lifeExp) %>% 
  select(country, year:lifeExp) %>% 
  write_csv(path = here::here("data", "europe-by-life-exp.csv"))

Plotting

The R language has extensive graphical capabilities.

Graphics in R may be created by many different methods including base graphics and more advanced plotting packages such as lattice.

The ggplot2 package was created by Hadley Wickham and provides a intuitive plotting system to rapidly generate publication quality graphics.

ggplot2 builds on the concept of the “Grammar of Graphics” (Wilkinson 2005, Bertin 1983) which describes a consistent syntax for the construction of a wide range of complex graphics by a concise description of their components.

Why use ggplot2?

The structured syntax and high level of abstraction used by ggplot2 should allow for the user to concentrate on the visualisations instead of creating the underlying code.

On top of this central philosophy ggplot2 has:

  • Increased flexibility over many plotting systems.
  • An advanced theme system for professional/publication level graphics.
  • Large developer base – Many libraries extending its flexibility.
  • Large user base – Great documentation and active mailing list.

It is always useful to think about the message you want to convey and the appropriate plot before writing any R code. Resources like this should help.

With some practice, ggplot2 makes it easier to go from the figure you are imagining in our head (or on paper) to a publication-ready image in R.

As with dplyr, we won’t have time to cover all details of ggplot2. This is however a useful cheatsheet that can be printed as a reference.

Basic plot types

A plot in ggplot2 is created with the following type of command

ggplot(data = <DATA>, mapping = aes(<MAPPINGS>)) +  <GEOM_FUNCTION>()

So we need to specify

  • The data to be used in graph
  • Mappings of data to the graph (aesthetic mapping)
  • What type of graph we want to use (The geom to use).

Lets say that we want to explore the relationship between GDP and Life Expectancy. We might start with the hypothesis that richer countries have higher life expectancy. A sensible choice of plot would be a scatter plot with gdp on the x-axis and life expectancy on the y-axis.

The first stage is to specify our dataset

library(ggplot2)
ggplot(data = gapminder)

For the aesthetics, as a bare minimum we will map the gdpPercap and lifeExp to the x- and y-axis of the plot

ggplot(data = gapminder,aes(x=gdpPercap, y=lifeExp))

That created the axes, but we still need to define how to display our points on the plot. As we have continuous data for both the x- and y-axis, geom_point is a good choice.

ggplot(data = gapminder,aes(x=gdpPercap, y=lifeExp)) + geom_point()

The geom we use will depend on what kind of data we have (continuous, categorical etc)

  • geom_point() - Scatter plots
  • geom_line() - Line plots
  • geom_smooth() - Fitted line plots
  • geom_bar() - Bar plots
  • geom_boxplot() - Boxplots
  • geom_jitter() - Jitter to plots
  • geom_histogram() - Histogram plots
  • geom_density() - Density plots
  • geom_text() - Text to plots
  • geom_errorbar() - Errorbars to plots
  • geom_violin() - Violin plots

Boxplots are commonly used to visualise the distributions of continuous data. We have to use a categorical variable on the x-axis. In the case of the gapminder data we might have to persuade ggplot2 that the year column is a factor rather than numerical data.

ggplot(gapminder, aes(x = as.factor(year), y=gdpPercap)) + geom_boxplot()

ggplot(gapminder, aes(x = gdpPercap)) + geom_histogram()

Counts with a barplot

ggplot(gapminder, aes(x=continent)) + geom_bar()




Exercise

  • Create a subset of the gapminder data frame containing just the rows for your country of birth
  • Has there been an increase in life expectancy over time?
    • visualise the trend using a scatter plot (geom_point), line graph (geom_line) or smoothed line (geom_smooth).



Customising the plot appearance

Our plots are a bit dreary at the moment, but one way to add colour is to add a col argument to the geom_point function. The value can be any of the pre-defined colour names in R. These are displayed in this handy online reference. Red, Green, Blue of Hex values can also be given.

ggplot(gapminder, aes(x = gdpPercap, y=lifeExp)) + geom_point(col="red")

However, a powerful feature of ggplot2 is that colours are treated as aesthetics of the plot. In other words we can use column in our dataset.

Let’s say that we want points on our plot to be coloured according to continent. We add an extra argument to the definition of aesthetics to define the mapping. ggplot2 will even decide on colours and create a legend for us.

ggplot(gapminder, aes(x = gdpPercap, y=lifeExp,col=continent)) + geom_point()

ggplot(gapminder, aes(x = year, y=gdpPercap,fill=as.factor(year))) + geom_boxplot()

Shape and size of points can also be mapped from the data. However, it is easy to get carried away.

ggplot(gapminder, aes(x = gdpPercap, y=lifeExp,shape=continent,size=pop,col=as.factor(year))) + geom_point()

ggplot(gapminder, aes(x = gdpPercap, y=lifeExp,col=continent)) + geom_point() + scale_color_brewer(palette = "Set2")

Facets

One very useful feature of ggplot is faceting. This allows you to produce plots subset by variables in your data. In the scatter plot above, it was quite difficult to see if the relationship between gdp and life expectancy was the same for each continent. To overcome this, we would like a see a separate plot for each continent.

To facet our data into multiple plots we can use the facet_wrap or facet_grid function and specify the variable we split by.

ggplot(gapminder, aes(x = gdpPercap, y=lifeExp,col=continent)) + geom_point() + facet_wrap(~continent)

The facet_grid function will create a grid-like plot with one variable on the x-axis and another on the y-axis.

ggplot(gapminder, aes(x = gdpPercap, y=lifeExp,col=continent)) + geom_point() + facet_wrap(continent~year)

Interaction between ggplot2 and dplyr

The previous plot was a bit messy as it contained all combinations of year and continent. Let’s suppose we want our analysis to be a bit more focussed and disregard countries in Oceania (as there are only 2 in our dataset) and years between 1997 and 2002. We should know how to restrict the rows from the gapminder dataset using the filter function. Instead of filtering the data, creating a new data frame and construcing the data frame from these new data we can use the%>% operator to create the data frame on the fly and pass directly to ggplot. Thus we don’t have to save a new data frame or alter the original data.

filter(gapminder, continent!="Oceania", year %in% c(1997,2002,2007)) %>% 
  ggplot(aes(x = gdpPercap, y=lifeExp,col=continent)) + geom_point() + facet_wrap(continent~year)

In another example we might want to look at how life expectancy has changed for particular countries.

filter(gapminder, country %in% c("United Kingdom", "France","Spain")) %>% 
  ggplot(aes(x = year, y=lifeExp,col=country)) + geom_point() + geom_line()

Summarising and grouping with dplyr

The summarise function can take any R function that takes a vector of values (i.e. a column from a data frame) and returns a single value. Some of the more useful functions include:

  • min minimum value
  • max maximum value
  • sum sum of values
  • mean mean value
  • sd standard deviation
  • median median value
  • IQR the interquartile range
  • n_distinct the number of distinct values
  • n the number of observations (Note: this is a special function that doesn’t take a vector argument, i.e. column)
summarise(gapminder, min(lifeExp), max(gdpPercap), mean(pop))

It is also possible to summarise using a function that takes more than one value, i.e. from multiple columns. For example, we could compute the correlation between year and life expectancy. Here we also assign names to the table that is produced.

summarise(gapminder, MinLifeExpectancy = min(lifeExp), 
          MaximumGDP = max(gdpPercap), 
          AveragePop=mean(pop), 
          Correlation=cor(year,lifeExp))

However, it is not particularly useful to calculate such values from the entire table as we have different continents and years. The group_by function allows us to split the table into different categories, and compute summary statistics. We can group the data according to year and compute the

group_by(gapminder, year) %>% summarise( MinLifeExpectancy = min(lifeExp), 
          MaximumGDP = max(gdpPercap), 
          AveragePop=mean(pop))

The nice thing about summarise is that it can followed up by any of the other dplyr verbs that we have met so far (select, filter, arrange..etc).

Returning to the correlation between life expectancy and year, we can summarise as follows:-

group_by(gapminder, country) %>% summarise(Correlation = cor(year ,lifeExp))

We can then arrange the table by the correlation to see which countries have the lowest correlation

group_by(gapminder, country) %>% summarise(Correlation = cor(year ,lifeExp)) %>% arrange(Correlation)

We can filter the results to find obsevations of interest

group_by(gapminder, country) %>% 
  summarise(Correlation = cor(year ,lifeExp)) %>% 
  filter(Correlation < 0)

The countries we identify could then be used as the basis for a plot.

filter(gapminder, country %in% c("Rwanda","Zambia","Zimbabwe")) %>% 
  ggplot(aes(x=year, y=lifeExp,col=country)) + geom_line()

Joining

In many real life situations, data are spread across multiple tables or spreadsheets. Usually this occurs because different types of information about a subject, e.g. a patient, are collected from different sources. It may be desirable for some analyses to combine data from two or more tables into a single data frame based on a common column, for example, an attribute that uniquely identifies the subject.

dplyr provides a set of join functions for combining two data frames based on matches within specified columns. For those familiar with such SQL, these operations are very similar to carrying out join operations between tables in a relational database.

As a toy example, lets consider two data frames that contain the names of various bands, and the instruments that they play:-

band_instruments
band_members

There are various ways in which we can join these two tables together. We will just consider the case of a “left join”.

Animated gif by Garrick Aden-Buie

left_join returns all rows from the first data frame regardless of whether there is a match in the second data frame. Rows with no match are included in the resulting data frame but have NA values in the additional columns coming from the second data frame.

Animations to illustrate other types of join are available at https://github.com/gadenbuie/tidy-animated-verbs

left_join(band_members,band_instruments)
Joining, by = "name"

right_join is similar but returns all rows from the second data frame that have a match with rows in the first data frame based on the specified column.

right_join(band_members,band_instruments)
Joining, by = "name"

inner_join only returns those rows where matches could be made

inner_join(band_members,band_instruments)
Joining, by = "name"



Exercise (open-ended)

  • The file medal_table.csv in the raw_data/ project sub-directory contains data about how many medals how been won by various countries at the Beijing summer olympics of 2008.
  • Read this csv file into R and join with the gapminder data from 2007
  • What interesting summaries / plots can you make from the data?



LS0tCnRpdGxlOiAiUiBDcmFzaCBDb3Vyc2UiCmF1dGhvcjogIk1hcmsgRHVubmluZyIKZGF0ZTogJ2ByIGZvcm1hdChTeXMudGltZSgpLCAiTGFzdCBtb2RpZmllZDogJWQgJWIgJVkiKWAnCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgojIEFja25vd2xlZGdlbWVudAoKVGhlc2UgbWF0ZXJpYWxzIGFyZSBhZGFwdGVkIGZyb20gYSBjb3Vyc2UgZGV2ZWxvcGVkIGF0IENhbmNlciBSZXNlYXJjaCBVayBDYW1icmlkZ2UgSW5zdGl0dXRlIGJ5IE1hcmsgRHVubmluZywgTWF0dGhldyBFbGRyaWRnZSBhbmQgVGhvbWFzIENhcnJvbGwuCgojIFIgYmFzaWNzCgojIyBBZHZhbnRhZ2VzIG9mIFIKCiFbXShpbWFnZXMvTllUaW1lc19SX0FydGljbGUucG5nKQoKVGhlIFIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UgaXMgbm93IHJlY29nbmlzZWQgYmV5b25kIHRoZSBhY2FkZW1pYyBjb21tdW5pdHkgYXMgYW4gZWZmZWN0IHNvbHV0aW9uIGZvciBkYXRhIGFuYWx5c2lzIGFuZCB2aXN1YWxpc2F0aW9uLiBbTm90YWJsZSB1c2VycyBvZiBSXShodHRwOi8vd3d3LnJldm9sdXRpb25hbmFseXRpY3MuY29tL2NvbXBhbmllcy11c2luZy1yKSBpbmNsdWRlOi0gCgotIFtGYWNlYm9va10oaHR0cDovL2Jsb2cucmV2b2x1dGlvbmFuYWx5dGljcy5jb20vMjAxMC8xMi9hbmFseXNpcy1vZi1mYWNlYm9vay1zdGF0dXMtdXBkYXRlcy5odG1sKSwKLSBbZ29vZ2xlXShodHRwOi8vYmxvZy5yZXZvbHV0aW9uYW5hbHl0aWNzLmNvbS8yMDA5LzA1L2dvb2dsZS11c2luZy1yLXRvLWFuYWx5emUtZWZmZWN0aXZlbmVzcy1vZi10di1hZHMuaHRtbCksCi0gW01pY3Jvc29mdF0oaHR0cDovL2Jsb2cucmV2b2x1dGlvbmFuYWx5dGljcy5jb20vMjAxNC8wNS9taWNyb3NvZnQtdXNlcy1yLWZvci14Ym94LW1hdGNobWFraW5nLmh0bWwpICh3aG8gcmVjZW50bHkgW2ludmVzdGVkXShodHRwOi8vYmxvZ3MubWljcm9zb2Z0LmNvbS9ibG9nLzIwMTUvMDEvMjMvbWljcm9zb2Z0LWFjcXVpcmUtcmV2b2x1dGlvbi1hbmFseXRpY3MtaGVscC1jdXN0b21lcnMtZmluZC1iaWctZGF0YS12YWx1ZS1hZHZhbmNlZC1zdGF0aXN0aWNhbC1hbmFseXNpcy8pIGluIGEgY29tbWVyaWNhbCBwcm92aWRlciBvZiBSKQotIFRoZSBbTmV3IFlvcmsgVGltZXNdKGh0dHA6Ly9ibG9nLnJldm9sdXRpb25hbmFseXRpY3MuY29tLzIwMTEvMDMvaG93LXRoZS1uZXcteW9yay10aW1lcy11c2VzLXItZm9yLWRhdGEtdmlzdWFsaXphdGlvbi5odG1sKS4KLSBbQnV6emZlZWRdKGh0dHA6Ly9ibG9nLnJldm9sdXRpb25hbmFseXRpY3MuY29tLzIwMTUvMTIvYnV6emZlZWQtdXNlcy1yLWZvci1kYXRhLWpvdXJuYWxpc20uaHRtbCkgdXNlIFIgZm9yIHNvbWUgb2YgdGhlaXIgc2VyaW91cyBhcnRpY2xlcyBhbmQgaGF2ZSBtYWRlIHRoZSBjb2RlIFtwdWJsaWNhbGx5IGF2YWlsYWJsZV0oaHR0cHM6Ly9idXp6ZmVlZG5ld3MuZ2l0aHViLmlvLzIwMTYtMDQtZmVkZXJhbC1zdXJ2ZWlsbGFuY2UtcGxhbmVzL2FuYWx5c2lzLmh0bWwpCi0gVGhlIFtOZXcgWmVhbGFuZCBUb3VyaXN0IEJvYXJkXShodHRwczovL21iaWVuei5zaGlueWFwcHMuaW8vdG91cmlzbV9kYXNoYm9hcmRfcHJvZC8pIGhhdmUgUiBydW5uaW5nIGluIHRoZSBiYWNrZ3JvdW5kIG9mIHRoZWlyIHdlYnNpdGUKLSBUaGUgQkJDIG1ha2VzIGNvZGUgYXZhaWxhYmxlIGZvciBzb21lIG9mIHRoZWlyIHN0b3JpZXMgKGUuZy4gW2dlbmRlciBiaWFzIGluIG11c2ljIGZlc3RpdmFsc10oaHR0cHM6Ly9naXRodWIuY29tL0JCQy1EYXRhLVVuaXQvbXVzaWMtZmVzdGl2YWxzKSkKLSBbQWlyYm5iXShodHRwczovL21lZGl1bS5jb20vYWlyYm5iLWVuZ2luZWVyaW5nL3VzaW5nLXItcGFja2FnZXMtYW5kLWVkdWNhdGlvbi10by1zY2FsZS1kYXRhLXNjaWVuY2UtYXQtYWlyYm5iLTkwNmZhYTU4ZTEyZCkKIVtdKGh0dHBzOi8vY2RuLWltYWdlcy0yLm1lZGl1bS5jb20vbWF4LzEyMDAvMSpCVE1iVkZoX2h6aUpKY2FRN1RCUndnLnBuZykKCiMjIEtleSBmZWF0dXJlcwoKLSBPcGVuLXNvdXJjZQotIENyb3NzLXBsYXRmb3JtCi0gQWNjZXNzIHRvIGV4aXN0aW5nIHZpc3VhbGlzYXRpb24gLyBzdGF0aXN0aWNhbCB0b29scwotIEZsZXhpYmlsaXR5Ci0gVmlzdWFsaXNhdGlvbiBhbmQgaW50ZXJhY3Rpdml0eQotIEFkZC1vbnMgZm9yIG1hbnkgZmllbGRzIG9mIHJlc2VhcmNoCi0gRmFjaWxpdGF0aW5nICoqKlJlcHJvZHVjaWJsZSBSZXNlYXJjaCoqKgoKIVtkdWtlLXNjYW5kYWxdKGltYWdlcy9yZXAtcmVzZWFyY2gtbnl0LnBuZykKClR3byBCaW9zdGF0aXNjaWFucyAobGF0ZXIgdGVybWVkICcqRm9yZW5zaWMgQmlvaW5mb3JtYXRpY2lhbnMqJykgZnJvbSBNLkQuIEFuZGVyc29uIHVzZWQgUiBleHRlbnNpdmVseSBkdXJpbmcgdGhlaXIgcmUtYW5hbHlzaXMgYW5kIGludmVzdGlnYXRpb24gb2YgYSBDbGluaWNhbCBQcm9nbm9zdGljYXRpb24gcGFwZXIgZnJvbSBEdWtlLiBUaGUgc3Vic2VxdWVudCBbc2NhbmRhbF0oaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1XNXNaVE5QTVFSTSkgcHV0IFJlcHJvZHVjaWJsZSBSZXNlYXJjaCBhdCB0aGUgZm9yZWZyb250IG9mIGV2ZXJ5b25lJ3MgbWluZC4KCktlaXRoIEJhZ2dlcmx5J3MgdGFsayBvbiB0aGUgc3ViamVjdCBpcyBoaWdoeS1yZWNvbW1lbmRlZC4KCjxpZnJhbWUgd2lkdGg9IjQyMCIgaGVpZ2h0PSIzMTUiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQvN2dZSXM3dVliTW8iIGZyYW1lYm9yZGVyPSIwIiBhbGxvd2Z1bGxzY3JlZW4+PC9pZnJhbWU+CgojIyBTdXBwb3J0IGZvciBSCgotIE9ubGluZSBmb3J1bXMgc3VjaCBhcyBbU3RhY2sgT3ZlcmZsb3ddKGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvdGFnZ2VkL3IpIHJlZ3VsYXJseSBmZWF0dXJlIFIKLSBbQmxvZ3NdKGh0dHA6Ly93d3cuci1ibG9nZ2Vycy5jb20vKQotIExvY2FsIHVzZXIgZ3JvdXBzCiAgKyBlLmcuIFtTaGVmZmllbGQtUl0oaHR0cHM6Ly93d3cubWVldHVwLmNvbS9TaGVmZmllbGRSLVNoZWZmaWVsZC1SLVVzZXJzLUdyb3VwLykKLSBEb2N1bWVudGF0aW9uIHZpYSBgP2Agb3IgYGhlbHAuc3RhcnQoKWAKLSBEb2N1bWVudGF0aW9uIGZvciBwYWNrYWdlcyBpcyBmb3VuZCB2aWEgdGhlIFBhY2thZ2VzIHRhYiBpbiB0aGUgYm90dG9tLXJpZ2h0IG9mIFJTdHVkaW8uCgoKIVtdKGltYWdlcy9sb2dvX2Jpb2NvbmR1Y3Rvci5wbmcpCgotICBQYWNrYWdlcyBhbmFseXNlIGFsbCBraW5kcyBvZiBHZW5vbWljIGRhdGEgKD44MDApCi0gQ29tcHVsc29yeSBkb2N1bWVudGF0aW9uICgqdmlnbmV0dGVzKikgZm9yIGVhY2ggcGFja2FnZQotIDYtbW9udGggcmVsZWFzZSBjeWNsZQotIFtDb3Vyc2UgTWF0ZXJpYWxzXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9oZWxwL2NvdXJzZS1tYXRlcmlhbHMvKQotIFtFeGFtcGxlIGRhdGFdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvQmlvY1ZpZXdzLmh0bWwjX19fRXhwZXJpbWVudERhdGEpIGFuZCBbd29ya2Zsb3dzXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9oZWxwL3dvcmtmbG93cy8pCi0gQ29tbW9uLCByZS11c2FibGUgZnJhbWV3b3JrIGFuZCBmdW5jdGlvbmFsaXR5Ci0gW0F2YWlsYWJsZSBTdXBwb3J0XShodHRwczovL3N1cHBvcnQuYmlvY29uZHVjdG9yLm9yZy8pCiAgICArIE9mdGVuIHlvdSB3aWxsIGJlIGFibGUgdG8gaW50ZXJhY3Qgd2l0aCB0aGUgcGFja2FnZSBtYWludGFpbmVycyAvIGRldmVsb3BlcnMgYW5kIG90aGVyIHBvd2VyLXVzZXJzIG9mIHRoZSBwcm9qZWN0IHNvZnR3YXJlCgogIAojIyBSU3R1ZGlvCgoKCi0gUnN0dWRpbyBpcyBhIGZyZWUgZW52aXJvbm1lbnQgZm9yIFIKLSBDb252ZW5pZW50IG1lbnVzIHRvIGFjY2VzcyBzY3JpcHRzLCBkaXNwbGF5IHBsb3RzCi0gU3RpbGwgbmVlZCB0byB1c2UgKmNvbW1hbmQtbGluZSogdG8gZ2V0IHRoaW5ncyBkb25lCi0gRGV2ZWxvcGVkIGJ5IHNvbWUgb2YgdGhlIGxlYWRpbmcgUiBwcm9ncmFtbWVycwotIFVzZWQgYnkgYmVnaW5uZXJzLCBhbmQgZXhwZXJpZW5jZWQgdXNlcnMgYWxpa2UKClRvIGdldCBzdGFydGVkLCB5b3Ugd2lsbCBuZWVkIHRvIGluc3RhbGwgdGhlIFtsYXRlc3QgdmVyc2lvbiBvZiBSXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy8pIGFuZCBbUlN0dWRpbyBEZXNrdG9wXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9wcm9kdWN0cy9yc3R1ZGlvL2Rvd25sb2FkMy8pOyBib3RoIG9mIHdoaWNoIGFyZSAqKipmcmVlKioqLiAKCk9uY2UgaW5zdGFsbGVkLCB5b3Ugc2hvdWxkIGJlIGFibGUgdG8gbGF1bmNoIFJTdHVkaW8gYnkgY2xpY2tpbmcgb24gaXRzIGljb246LQoKCiFbXShodHRwOi8vd3d3LnJzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE0LzAzL2JsdWUtMTI1LnBuZykKCiMjIEVudGVyaW5nIGNvbW1hbmRzIGluIFIKCi0gVGhlIHRyYWRpdGlvbmFsIHdheSB0byBlbnRlciBSIGNvbW1hbmRzIGlzIHZpYSB0aGUgVGVybWluYWwsIG9yIHVzaW5nIHRoZSBjb25zb2xlIGluIFJTdHVkaW8gKGJvdHRvbS1sZWZ0IHBhbmVsIHdoZW4gUlN0dWRpbyBvcGVucyBmb3IgZmlyc3QgdGltZSkuCiAgKyB0aGlzIGRvZXNuJ3QgYXV0b21hdGljYWxseSBrZWVwIHRyYWNrIG9mIHRoZSBzdGVwcyB5b3UgZGlkCi0gQWx0ZXJuYXRpdmUsIGFuICpSIHNjcmlwdCogY2FuIGJlIHVzZWQgdG8ga2VlcCBhIHJlY29yZCBvZiB0aGUgY29tbWFuZHMgeW91IHVzZWQuCi0gVGhlIFIgY29kZSBjYW4gYmUgcnVuIGZyb20gaW5zaWRlIHRoZSBzY3JpcHQgYW5kIHRoZSByZXN1bHRzIGFyZSBkaXNwbGF5ZWQgaW4gdGhlIGNvbnNvbGUgb3IgVmlld2VyIHBhbmVsIChlZyBwbG90cykKLSBFYWNoIGxpbmUgb2YgUiBjb2RlIGNhbiBiZSBleGVjdXRlZCBieSBjbGlja2luZyBvbiB0aGUgbGluZSBhbmQgcHJlc3NpbmcgQ1RSTCBhbmQgRU5URVIKLSBUcnkgdGhpcyBub3chCgpgYGB7cn0KcHJpbnQoIkhlbGxvIFdvcmxkIikKYGBgCgotIFlvdSBjYW4gYWRkIFIgY2h1bmtzIGJ5IHByZXNzaW5nIENSVEwgKyBBTFQgKyBJCiAgICArIG9yIHVzaW5nIHRoZSBJbnNlcnQgbWVudSBvcHRpb24KICAgICsgKGNhbiBhbHNvIGluY2x1ZGUgY29kZSBmcm9tIG90aGVyIGxhbmd1YWdlcyBzdWNoIGFzIFB5dGhvbiBvciBiYXNoKQogICAgKyAqKnRyeSBhbmQgYXZvaWQgYWRkaW5nIGNvZGUgY2h1bmtzIG1hbnVhbGx5KioKCgojIEdldHRpbmcgc3RhcnRlZAoKIVtdKGltYWdlcy8xMjhweC1TSEFSUF9FTFNJTUFURV9FTC1XMjIxLmpwZykKCkF0IGEgYmFzaWMgbGV2ZWwsIHdlIGNhbiB1c2UgUiBhcyBhIGNhbGN1bGF0b3IgdG8gY29tcHV0ZSBzaW1wbGUgc3VtcyB3aXRoIHRoZSBgK2AsIGAtYCwgYCpgIChmb3IgbXVsdGlwbGljYXRpb24pIGFuZCBgL2AgKGZvciBkaXZpc2lvbikgc3ltYm9scy4gCgpgYGB7cn0KMiArIDIKMiAtIDIKNCAqIDMKMTAgLyAyCmBgYAoKVGhlIGFuc3dlciBpcyBkaXNwbGF5ZWQgYXQgdGhlIGNvbnNvbGUgd2l0aCBhIGBbMV1gIGluIGZyb250IG9mIGl0LiBUaGUgYDFgIGluc2lkZSB0aGUgc3F1YXJlIGJyYWNrZXRzIGlzIGEgcGxhY2UtaG9sZGVyIHRvIHNpZ25pZnkgaG93IG1hbnkgdmFsdWVzIHdlcmUgaW4gdGhlIGFuc3dlciAoaW4gdGhpcyBjYXNlIG9ubHkgb25lKS4gV2Ugd2lsbCB0YWxrIGFib3V0IGRlYWxpbmcgd2l0aCBsaXN0cyBvZiBudW1iZXJzIHNob3J0bHkuLi4KCkluIHRoZSBjYXNlIG9mIGV4cHJlc3Npb25zIGludm9sdmluZyBtdWx0aXBsZSBvcGVyYXRpb25zLCBSIHJlc3BlY3RzIHRoZSBbQk9ETUFTXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9PcmRlcl9vZl9vcGVyYXRpb25zI01uZW1vbmljcykgc3lzdGVtIHRvIGRlY2lkZSB0aGUgb3JkZXIgaW4gd2hpY2ggb3BlcmF0aW9ucyBzaG91bGQgYmUgcGVyZm9ybWVkLgoKYGBge3J9CjIgKyAyICozCjIgKyAoMiAqIDMpCigyICsgMikgKiAzCmBgYAoKUiBpcyBjYXBhYmxlIG9mIG1vcmUgY29tcGxpY2F0ZWQgYXJpdGhtZXRpYyBzdWNoIGFzIHRyaWdvbm9tZXRyeSBhbmQgbG9nYXJpdGhtczsgbGlrZSB5b3Ugd291bGQgZmluZCBvbiBhIGZhbmN5IHNjaWVudGlmaWMgY2FsY3VsYXRvci4gT2YgY291cnNlLCBSIGFsc28gaGFzIGEgcGxldGhvcmEgb2Ygc3RhdGlzdGljYWwgb3BlcmF0aW9ucyBhcyB3ZSB3aWxsIHNlZS4KCiFbXShpbWFnZXMvMTI4cHgtQ2FzaW8tZngxMTVFUy01NTY0LmpwZykKCmBgYHtyfQpwaQpzaW4gKHBpLzIpCmNvcyhwaSkKdGFuKDIpCmxvZygxKQpgYGAKCldlIGNhbiBvbmx5IGdvIHNvIGZhciB3aXRoIHBlcmZvcm1pbmcgc2ltcGxlIGNhbGN1bGF0aW9ucyBsaWtlIHRoaXMuIEV2ZW50dWFsbHkgd2Ugd2lsbCBuZWVkIHRvIHN0b3JlIG91ciByZXN1bHRzIGZvciBsYXRlciB1c2UuIEZvciB0aGlzLCB3ZSBuZWVkIHRvIG1ha2UgdXNlIG9mICp2YXJpYWJsZXMqLgoKIyMgVmFyaWFibGVzCgpBIHZhcmlhYmxlIGlzIGEgbGV0dGVyIG9yIHdvcmQgd2hpY2ggdGFrZXMgKG9yIGNvbnRhaW5zKSBhIHZhbHVlLiBXZQp1c2UgdGhlIGFzc2lnbm1lbnQgJ29wZXJhdG9yJywgYDwtYCB0byBjcmVhdGUgYSB2YXJpYWJsZSBhbmQgc3RvcmUgc29tZSB2YWx1ZSBpbiBpdC4gCgpgYGB7cn0KeCA8LSAxMAp4Cm15TnVtYmVyIDwtIDI1Cm15TnVtYmVyCmBgYApXZSBhbHNvIGNhbiBwZXJmb3JtIGFyaXRobWV0aWMgb24gdmFyaWFibGVzIHVzaW5nIGZ1bmN0aW9uczoKCmBgYHtyfQpzcXJ0KG15TnVtYmVyKQpgYGAKCldlIGNhbiBhZGQgdmFyaWFibGVzIHRvZ2V0aGVyOgpgYGB7cn0KeCArIG15TnVtYmVyCmBgYAoKCldlIGNhbiBjaGFuZ2UgdGhlIHZhbHVlIG9mIGFuIGV4aXN0aW5nIHZhcmlhYmxlOgoKYGBge3J9CnggPC0gMjEKeApgYGAKCi0gV2UgY2FuIHNldCBvbmUgdmFyaWFibGUgdG8gZXF1YWwgdGhlIHZhbHVlIG9mIGFub3RoZXIgdmFyaWFibGU6CgpgYGB7cn0KeCA8LSBteU51bWJlcgp4CmBgYAoKLSBXZSBjYW4gbW9kaWZ5IHRoZSBjb250ZW50cyBvZiBhIHZhcmlhYmxlOgoKYGBge3J9Cm15TnVtYmVyIDwtIG15TnVtYmVyICsgc3FydCgxNikKbXlOdW1iZXIKYGBgCgpXaGVuIHdlIGFyZSBmZWVsaW5nIGxhenkgd2UgbWlnaHQgZ2l2ZSBvdXIgdmFyaWFibGVzIHNob3J0IG5hbWVzIChgeGAsIGB5YCwgYGlgLi4uZXRjKSwgYnV0IGEgYmV0dGVyIHByYWN0aWNlIHdvdWxkIGJlIHRvIGdpdmUgdGhlbSBtZWFuaW5nZnVsIG5hbWVzLiBUaGVyZSBhcmUgc29tZSByZXN0cmljdGlvbnMgb24gY3JlYXRpbmcgdmFyaWFibGUgbmFtZXMuIFRoZXkgY2Fubm90IHN0YXJ0IHdpdGggYSBudW1iZXIgb3IgY29udGFpbiBjaGFyYWN0ZXJzIHN1Y2ggYXMgYC5gLCBgX2AsICctJy4gTmFtaW5nIHZhcmlhYmxlcyB0aGUgc2FtZSBhcyBpbi1idWlsdCBmdW5jdGlvbnMgaW4gUiwgc3VjaCBhcyBgY2AsIGBUYCwgYG1lYW5gIHNob3VsZCBhbHNvIGJlIGF2b2lkZWQuCgpOYW1pbmcgdmFyaWFibGVzIGlzIGEgbWF0dGVyIG9mIHRhc3RlLiBTb21lIFtjb252ZW50aW9uc10oaHR0cDovL2Fkdi1yLmhhZC5jby5uei9TdHlsZS5odG1sKSBleGlzdCBzdWNoIGFzIGEgc2VwYXJhdGluZyB3b3JkcyB3aXRoIGAtYCBvciB1c2luZyAqYyphbWVsKkMqYXBzLiBXaGF0ZXZlciBjb252ZW50aW9uIHlvdSBkZWNpZGVkLCBzdGljayB3aXRoIGl0IQoKIyMgRnVuY3Rpb25zCgoqKkZ1bmN0aW9ucyoqIGluIFIgcGVyZm9ybSBvcGVyYXRpb25zIG9uICoqYXJndW1lbnRzKiogKHRoZSBpbnB1dHMocykgdG8gdGhlIGZ1bmN0aW9uKS4gV2UgaGF2ZSBhbHJlYWR5IHVzZWQ6CgpgYGB7cn0Kc2luKHgpCmBgYAoKdGhpcyByZXR1cm5zIHRoZSBzaW5lIG9mIHguIEluIHRoaXMgY2FzZSB0aGUgZnVuY3Rpb24gaGFzIG9uZSBhcmd1bWVudDogKip4KiouIEFyZ3VtZW50cyBhcmUgYWx3YXlzIGNvbnRhaW5lZCBpbiBwYXJlbnRoZXNlcyAtLSBjdXJ2ZWQgYnJhY2tldHMsICoqKCkqKiAtLSBzZXBhcmF0ZWQgYnkgY29tbWFzLgoKCkFyZ3VtZW50cyBjYW4gYmUgbmFtZWQgb3IgdW5uYW1lZCwgYnV0IGlmIHRoZXkgYXJlIHVubmFtZWQgdGhleSBtdXN0IGJlIG9yZGVyZWQgKHdlIHdpbGwgc2VlIGxhdGVyIGhvdyB0byBmaW5kIHRoZSByaWdodCBvcmRlcikuIFRoZSBuYW1lcyBvZiB0aGUgYXJndW1lbnRzIGFyZSBkZXRlcm1pbmVkIGJ5IHRoZSBhdXRob3Igb2YgdGhlIGZ1bmN0aW9uIGFuZCBjYW4gYmUgZm91bmQgaW4gdGhlIGhlbHAgcGFnZSBmb3IgdGhlIGZ1bmN0aW9uLiBXaGVuIHRlc3RpbmcgY29kZSwgaXQgaXMgZWFzaWVyIGFuZCBzYWZlciB0byBuYW1lIHRoZSBhcmd1bWVudHMuIGBzZXFgIGlzIGEgZnVuY3Rpb24gZm9yIGdlbmVyYXRpbmcgYSBudW1lcmljIHNlcXVlbmNlICpmcm9tKiBhbmQgKnRvKiBwYXJ0aWN1bGFyIG51bWJlcnMuIFR5cGUgYD9zZXFgIHRvIGdldCB0aGUgaGVscCBwYWdlIGZvciB0aGlzIGZ1bmN0aW9uLgoKYGBge3J9CnNlcShmcm9tID0gMywgdG8gPSAyMCwgYnkgPSA0KQpzZXEoMywgMjAsIDQpCmBgYAoKQXJndW1lbnRzIGNhbiBoYXZlICpkZWZhdWx0KiB2YWx1ZXMsIG1lYW5pbmcgd2UgZG8gbm90IG5lZWQgdG8gc3BlY2lmeSB2YWx1ZXMgZm9yIHRoZXNlIGluIG9yZGVyIHRvIHJ1biB0aGUgZnVuY3Rpb24uCgpgcm5vcm1gIGlzIGEgZnVuY3Rpb24gdGhhdCB3aWxsIGdlbmVyYXRlIGEgc2VyaWVzIG9mIHZhbHVlcyBmcm9tIGEgKm5vcm1hbCBkaXN0cmlidXRpb24qLiBJbiBvcmRlciB0byB1c2UgdGhlIGZ1bmN0aW9uLCB3ZSBuZWVkIHRvIHRlbGwgUiBob3cgbWFueSB2YWx1ZXMgd2Ugd2FudAoKYGBge3J9CiMjIHRoaXMgd2lsbCBwcm9kdWNlIGEgcmFuZG9tIHNldCBvZiBudW1iZXJzLCBzbyBldmVyeW9uZSB3aWxsIGdldCBhIGRpZmZlcmVudCBzZXQgb2YgbnVtYmVycwpybm9ybShuPTEwKQpgYGAKClRoZSBub3JtYWwgZGlzdHJpYnV0aW9uIGlzIGRlZmluZWQgYnkgYSAqbWVhbiogKGF2ZXJhZ2UpIGFuZCAqc3RhbmRhcmQgZGV2aWF0aW9uKiAoc3ByZWFkKS4gSG93ZXZlciwgaW4gdGhlIGFib3ZlIGV4YW1wbGUgd2UgZGlkbid0IHRlbGwgUiB3aGF0IG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiB3ZSB3YW50ZWQuIFNvIGhvdyBkb2VzIFIga25vdyB3aGF0IHRvIGRvPyBBbGwgYXJndW1lbnRzIHRvIGEgZnVuY3Rpb24gYW5kIHRoZWlyIGRlZmF1bHQgdmFsdWVzIGFyZSBsaXN0ZWQgaW4gdGhlIGhlbHAgcGFnZQoKKCpOLkIgc29tZXRpbWVzIGhlbHAgcGFnZXMgY2FuIGRlc2NyaWJlIG1vcmUgdGhhbiBvbmUgZnVuY3Rpb24qKQoKYGBge3J9Cj9ybm9ybQpgYGAKCkluIHRoaXMgY2FzZSwgd2Ugc2VlIHRoYXQgdGhlIGRlZmF1bHRzIGZvciBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gYXJlIDAgYW5kIDEuIFdlIGNhbiBjaGFuZ2UgdGhlIGZ1bmN0aW9uIHRvIGdlbmVyYXRlIHZhbHVlcyBmcm9tIGEgZGlzdHJpYnV0aW9uIHdpdGggYSBkaWZmZXJlbnQgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIHVzaW5nIHRoZSBgbWVhbmAgYW5kIGBzZGAgKmFyZ3VtZW50cyouIEl0IGlzIGltcG9ydGFudCB0aGF0IHdlIGdldCB0aGUgc3BlbGxpbmcgb2YgdGhlc2UgYXJndW1lbnRzIGV4YWN0bHkgcmlnaHQsIG90aGVyd2lzZSBSIHdpbGwgYW4gZXJyb3IgbWVzc2FnZSwgb3IgKHdvcnNlPykgZG8gc29tZXRoaW5nIHVuZXhwZWN0ZWQuCgpgYGB7cn0Kcm5vcm0obj0xMCwgbWVhbj0yLHNkPTMpCnJub3JtKDEwLCAyLCAzKQpgYGAKCkluIHRoZSBleGFtcGxlcyBhYm92ZSwgYHNlcWAgYW5kIGBybm9ybWAgd2VyZSBib3RoIG91dHB1dHRpbmcgYSBzZXJpZXMgb2YgbnVtYmVycywgd2hpY2ggaXMgY2FsbGVkIGEgKnZlY3RvciogaW4gUiBhbmQgaXMgdGhlIG1vc3QtZnVuZGFtZW50YWwgZGF0YS10eXBlLgoKCioqKioqKgoqKioqKioKKioqKioqCgoKCiMjIyBFeGVyY2lzZQoKCiAgLSBXaGF0IGlzIHRoZSB2YWx1ZSBvZiBgcGlgIHRvIDMgZGVjaW1hbCBwbGFjZXM/CiAgICArIHNlZSB0aGUgaGVscCBmb3IgYHJvdW5kYCBgP3JvdW5kYAogIC0gSG93IGNhbiB3ZSBhIGNyZWF0ZSBhIHNlcXVlbmNlIGZyb20gMiB0byAyMCBjb21wcmlzZWQgb2YgNSBlcXVhbGx5LXNwYWNlZCBudW1iZXJzPwogICAgKyBjaGVjayB0aGUgaGVscCBwYWdlIGZvciBzZXEgYD9zZXFgCiAgLSBDcmVhdGUgYSAqdmFyaWFibGUqIGNvbnRhaW5pbmcgMTAwMCByYW5kb20gbnVtYmVycyB3aXRoIGEgKm1lYW4qIG9mIDIgYW5kIGEgKnN0YW5kYXJkIGRldmlhdGlvbiogb2YgMwogICAgKyB3aGF0IGlzIHRoZSBtYXhpbXVtIGFuZCBtaW5pbXVtIG9mIHRoZXNlIG51bWJlcnM/CiAgICArIHdoYXQgaXMgdGhlIGF2ZXJhZ2U/CiAgICArIEhJTlQ6IHNlZSB0aGUgaGVscCBwYWdlcyBmb3IgZnVuY3Rpb25zIGBtaW5gLCBgbWF4YCBhbmQgYG1lYW5gCiAgICAKYGBge3J9CgoKYGBgCiAgICAKICAgIAoqKioqKioKKioqKioqCioqKioqKgoKCiMjIFBhY2thZ2VzIGluIFIKClNvIGZhciB3ZSBoYXZlIHVzZWQgZnVuY3Rpb25zIHRoYXQgYXJlIGF2YWlsYWJsZSB3aXRoIHRoZSAqYmFzZSogZGlzdHJpYnV0aW9uIG9mIFI7IHRoZSBmdW5jdGlvbnMgeW91IGdldCB3aXRoIGEgY2xlYW4gaW5zdGFsbCBvZiBSLiBUaGUgb3Blbi1zb3VyY2UgbmF0dXJlIG9mIFIgZW5jb3VyYWdlcyBvdGhlcnMgdG8gd3JpdGUgdGhlaXIgb3duIGZ1bmN0aW9ucyBmb3IgdGhlaXIgcGFydGljdWxhciBkYXRhLXR5cGUgb3IgYW5hbHlzZXMuCgpQYWNrYWdlcyBhcmUgZGlzdHJpYnV0ZWQgdGhyb3VnaCAqcmVwb3NpdG9yaWVzKi4gVGhlIG1vc3QtY29tbW9uIG9uZXMgYXJlIENSQU4gYW5kIEJpb2NvbmR1Y3Rvci4gQ1JBTiBhbG9uZSBoYXMgbWFueSB0aG91c2FuZHMgb2YgcGFja2FnZXMuCgpUaGUgKipQYWNrYWdlcyoqIHRhYiBpbiB0aGUgYm90dG9tLXJpZ2h0IHBhbmVsIG9mIFJTdHVkaW8gbGlzdHMgYWxsIHBhY2thZ2VzIHRoYXQgeW91IGN1cnJlbnRseSBoYXZlIGluc3RhbGxlZC4gQ2xpY2tpbmcgb24gYSBwYWNrYWdlIG5hbWUgd2lsbCBzaG93IGEgbGlzdCBvZiBmdW5jdGlvbnMgdGhhdCBhdmFpbGFibGUgb25jZSB0aGF0IHBhY2thZ2UgaGFzIGJlZW4gbG9hZGVkLiAKClRoZXJlIGFyZSBmdW5jdGlvbnMgZm9yIGluc3RhbGxpbmcgcGFja2FnZXMgd2l0aGluIFIuIElmIHlvdXIgcGFja2FnZSBpcyBwYXJ0IG9mIHRoZSBtYWluICoqQ1JBTioqIHJlcG9zaXRvcnksIHlvdSBjYW4gdXNlIGBpbnN0YWxsLnBhY2thZ2VzYAoKV2Ugd2lsbCBiZSB1c2luZyB0aGUgYHRpZHl2ZXJzZWAgUiBwYWNrYWdlIGluIHRoaXMgcHJhY3RpY2FsLiBUbyBpbnN0YWxsIGl0LCB3ZSB3b3VsZCBkby4KCmBgYHtyIGV2YWw9RkFMU0V9Cmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCmBgYAoKCkEgcGFja2FnZSBtYXkgaGF2ZSBzZXZlcmFsICpkZXBlbmRlbmNpZXMqOyBvdGhlciBSIHBhY2thZ2VzIGZyb20gd2hpY2ggaXQgdXNlcyBmdW5jdGlvbnMgb3IgZGF0YSB0eXBlcyAocmUtdXNpbmcgY29kZSBmcm9tIG90aGVyIHBhY2thZ2VzIGlzIHN0cm9uZ2x5LWVuY291cmFnZWQpLiBJZiB0aGlzIGlzIHRoZSBjYXNlLCB0aGUgb3RoZXIgUiBwYWNrYWdlcyB3aWxsIGJlIGxvY2F0ZWQgYW5kIGluc3RhbGxlZCB0b28uCgoqKlNvIGxvbmcgYXMgeW91IHN0aWNrIHdpdGggdGhlIHNhbWUgdmVyc2lvbiBvZiBSLCB5b3Ugd29uJ3QgbmVlZCB0byByZXBlYXQgdGhpcyBpbnN0YWxsIHByb2Nlc3MuKioKCgpPbmNlIGEgcGFja2FnZSBpcyBpbnN0YWxsZWQsIHRoZSBgbGlicmFyeWAgZnVuY3Rpb24gaXMgdXNlZCB0byBsb2FkIGEgcGFja2FnZSBhbmQgbWFrZSBpdCdzIGZ1bmN0aW9ucyAvIGRhdGEgYXZhaWxhYmxlIGluIHlvdXIgY3VycmVudCBSIHNlc3Npb24uICpZb3UgbmVlZCB0byBkbyB0aGlzIGV2ZXJ5IHRpbWUgeW91IGxvYWQgYSBuZXcgUlN0dWRpbyBzZXNzaW9uKi4gTGV0J3MgZ28gYWhlYWQgYW5kIGxvYWQgdGhlIGB0aWR5dmVyc2VgLgoKCmBgYHtyfQojIyB0aWR5dmVyc2UgaXMgYSBjb2xsZWN0aW9uIG9mIHBhY2thZ2VzIGZvciBkYXRhIG1hbmlwdWxhdGlvbiBhbmQgdmlzdWFsaXNhdGlvbgpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgoKCiMgRGVhbGluZyB3aXRoIGRhdGEKClRoZSBbKioqdGlkeXZlcnNlKioqXShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnLykgaXMgaW4gZmFjdCBhbiBlY28tc3lzdGVtIG9mIHBhY2thZ2VzIHRoYXQgcHJvdmlkZXMgYSBjb25zaXN0ZW50LCBpbnR1aXRpdmUgc3lzdGVtIGZvciBkYXRhIG1hbmlwdWxhdGlvbiBhbmQgdmlzdWFsaXNhdGlvbiBpbiBSLgoKCiFbXShodHRwczovL2FiZXJkZWVuc3R1ZHlncm91cC5naXRodWIuaW8vc3R1ZHlHcm91cC9sZXNzb25zL1NHLVQyLUpvaW50V29ya3Nob3AvdGlkeXZlcnNlLnBuZykKX0ltYWdlIENyZWRpdDpfIFsqKipBYmVyZGVlbiBTdHVkeSBHcm91cCoqKl0oaHR0cHM6Ly9hYmVyZGVlbnN0dWR5Z3JvdXAuZ2l0aHViLmlvL3N0dWR5R3JvdXAvbGVzc29ucy9TRy1UMi1Kb2ludFdvcmtzaG9wL1BvcHVsYXRpb25DaGFuZ2VTcGVjaWVzT2NjdXJyZW5jZS8pCgpXZSBhcmUgZ29pbmcgdG8gZXhwbG9yZSBzb21lIG9mIHRoZSBiYXNpYyBmZWF0dXJlcyBvZiB0aGUgYHRpZHl2ZXJzZWAgdXNpbmcgZGF0YSBmcm9tIHRoZSBbZ2FwbWluZGVyXShodHRwczovL3d3dy5nYXBtaW5kZXIub3JnL2RhdGEvKSBwcm9qZWN0LCB3aGljaCBoYXZlIGJlZW4gYnVuZGxlZCBpbnRvIGFuIFtSIHBhY2thZ2VdKGh0dHBzOi8vZ2l0aHViLmNvbS9qZW5ueWJjL2dhcG1pbmRlcikuIFRoZXNlIGRhdGEgZ2l2ZSB2YXJpb3VzIGluZGljYXRvciB2YXJpYWJsZXMgZm9yIGRpZmZlcmVudCBjb3VudHJpZXMgYXJvdW5kIHRoZSB3b3JsZCAobGlmZSBleHBlY3RhbmN5LCBwb3B1bGF0aW9uIGFuZCBHcm9zcyBEb21lc3RpYyBQcm9kdWN0KS4gV2UgaGF2ZSBzYXZlZCB0aGVzZSBkYXRhIGFzIGEgYC5jc3ZgIGZpbGUgY2FsbGVkIGBnYXBtaWRlci5jc3ZgIGluIGEgc3ViLWRpcmVjdG9yeSBjYWxsZWQgYHJhd19kYXRhL2AgdG8gZGVtb25zdHJhdGUgaG93IHRvIGltcG9ydCBkYXRhIGludG8gUi4KCgpZb3UgY2FuIGRvd25sb2FkIHRoZXNlIGRhdGEsIGFsb25nIHdpdGggdGhlIHJlc3Qgb2YgdGhlIG1hdGVyaWFsIG5lZWRlZCBmb3IgdG9kYXkncyB3b3Jrc2hvcCBhbmQgYSBjb3B5IG9mIHRoaXMgaGFuZG91dCAgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9hbm5ha3J5c3RhbGxpL3ItY3Jhc2gtY291cnNlL3Jhdy9tYXN0ZXIvQ291cnNlRGF0YS56aXApLiBTYXZlIHRoZSBgLnppcGAgZmlsZSBzb21ld2hlcmUgb24geW91ciBjb21wdXRlciBhbmQgdW56aXAgaXQuCgoKCiMjIFdvcmtpbmcgaW4gUnN0dWRpbyBQcm9qZWN0cwoKV2UgYXJlIGFsc28gZ29pbmcgdG8gd29ya2luZyBpbiBhbiBSc3R1ZGlvIFtQcm9qZWN0XShodHRwczovL3N1cHBvcnQucnN0dWRpby5jb20vaGMvZW4tdXMvYXJ0aWNsZXMvMjAwNTI2MjA3LVVzaW5nLVByb2plY3RzKS4gV2Ugc3VnZ2VzdCB5b3Ugb3JnYW5pemUgZWFjaCBkYXRhIGFuYWx5c2lzIGludG8gYSBwcm9qZWN0OiBhIGZvbGRlciBvbiB5b3VyIGNvbXB1dGVyIGNvbnRhaW5pbmcgYWxsIGZpbGVzIHJlbGV2YW50IHRvIGEgcGFydGljdWxhciBwaWVjZSBvZiB3b3JrLgpUaGVyZSBhcmUgYSBudW1iZXIgb2YgYmVuZWZpdHMgdG8gdGhpcyBwcmFjdGljZSBpbiBnZW5lcmFsLCBhbmQgdGhlIFJzdHVkaW8gaW1wbGVtZW50YXRpb24gaW4gcGFydGljdWxhciwgc3VtbWFyaXNlZCBpbiBKZW5ueSBCcnlhbidzIGJsb2dwb3N0IG9uIFtQcm9qZWN0LW9yaWVudGVkIHdvcmtmbG93c10oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy9hcnRpY2xlcy8yMDE3LzEyL3dvcmtmbG93LXZzLXNjcmlwdC8pLgoKTWFrZXMgd29yazogCgotIFNlbGYtY29udGFpbmVkCi0gUG9ydGFibGUKClJTdHVkaW8gZnVsbHkgc3VwcG9ydHMgUHJvamVjdC1iYXNlZCB3b3JrZmxvd3MsIG1ha2luZyBpdCBlYXN5IHRvIHN3aXRjaCBmcm9tIG9uZSB0byBhbm90aGVyLCBoYXZlIG1hbnkgcHJvamVjdHMgb3BlbiBhdCBvbmNlLCByZS1sYXVuY2ggcmVjZW50bHkgdXNlZCBQcm9qZWN0cywgZXRjLgoKVGhlcmUgYXJlIGEgZmV3IHdheXMgdG8gY3JlYXRlIG5ldyBQcm9qZWN0cy4gV2UgY2FuIHN0YXJ0IHdpdGggYSBuZXcgUHJvamVjdCBpbiBhIG5lZSBkaXJlY3RvcnkgYnV0IHdlIGNhbiBhbHNvIHR1cm4gYW4gZXhpc3RpbmcgZGlyZWN0b3J5IGludG8gYSBQcm9qZWN0LiBMZXQncyBkbyB0aGlzIHdpdGggdGhlIHVuemlwcGVkIGZvbGRlciBjb250YWluaW5nIHRoZSB3b3Jrc2hvcCBtYXRlcmlhbHMgd2UganVzdCBkb3dubG9hZGVkLgoKCjxkaXYgY2xhc3M9ImFsZXJ0IGFsZXJ0LWluZm8iPgoKIyMjIyAqKkZpbGUgPiBOZXcgUHJvamVjdCA+IEV4aXN0aW5nIERpcmVjdG9yeSA+IC4uLioqIApfY2hvb3NlIHdvcmtzaG9wIG1hdGVyaWFsIGRpcmVjdG9yeV8KCjwvZGl2PgoKV2UndmUgbm93IHR1cm5lZCBvdXIgd29ya3Nob3AgbWF0ZXJpYWwgZm9sZGVyIGludG8gYW4gUnN0dWRpbyBwcm9qZWN0IGFuZCBsYXVuY2hlZCBpdCwgd2hpY2ggbWVhbnM6CgotIGEgKipmcmVzaCBSIHNlc3Npb24gaGFzIGJlZW4gbGF1bmNoZWQqKiAoc2VlIGhvdyB0aGUgKipFbnZpcm9ubWVudCoqIHRhYiBpcyBub3cgY2xlYXIpCi0gdGhlICoqd29ya2luZyBkaXJlY3RvcnkgaGFzIGJlZW4gc2V0IHRvIHRoZSBwcm9qZWN0IHJvb3QqKiAoeW91IGNhbiBjaGVjayB0aGlzIGZvcm0gdGhlIGhlYWRlciBvbiB0aGUgQ29uc29sZSB0YWIpCi0gQSBwcm9qZWN0IHNwZWNpZmljICoqSGlzdG9yeSoqIGlzIGluaXRpYXRlZC4KCgojIyBXb3JraW5nIGluIFIgTm90ZWJvb2tzCgpXZSdsbCBhbHNvIGJlIHdvcmtpbmcgaW4gYW4gUiBOb3RlYm9vay4gVGhlc2UgZmlsZSBhcmUgYW4gUiBNYXJrZG93biBkb2N1bWVudCB0eXBlLCB3aGljaCBjb21iaW5lIFIgY29kZSB3aXRoIG1hcmtkb3duLCBhIGRvY3VtZW50YXRpb24gbGFuZ3VhZ2UsIHByb3ZpZGluZyBhIGZyYW1ld29yayBmb3IgbGl0ZXJhdGUgcHJvZ3JhbW1pbmcuICBJbiBhbiBSIE5vdGVib29rLCBSIGNvZGUgY2h1bmtzIGNhbiBiZSBleGVjdXRlZCBpbmRlcGVuZGVudGx5IGFuZCBpbnRlcmFjdGl2ZWx5LCB3aXRoIG91dHB1dCB2aXNpYmxlIGltbWVkaWF0ZWx5IGJlbmVhdGggdGhlIGlucHV0LgoKTGV0J3Mgb3BlbiBhbiBSIE5vdGVib29rIHRvIHN0YXJ0IHdvcmsgaW4uCgo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC1pbmZvIj4KCiMjIyMgKipGaWxlID4gTmV3IEZpbGUgPiBSIE5vdGVib29rKiogCgotIF9naXZlIGl0IGEgdGl0bGVfCi0gX3NhdmUgaXRfCgo8L2Rpdj4KCkVhY2ggY2h1bmsgb2YgUiBjb2RlIGxvb2tzIHNvbWV0aGluZyBsaWtlIHRoaXMuCgogICAgYHIgJydgYGBge3IgZm9vfQogICAgcHJpbnQoJ2hlbGxvIHdvcmxkIScpCiAgICBgYGAKCk5ldyBSIGNvZGUgY2h1bmtzIGNhbiBiZSBpbnNlcnRlZCBieTogCgotIGtleWJvYXJkIHNob3J0Y3V0OiBgQ3RybCArIEFsdCArIElgIChtYWNPUzogYENtZCArIE9wdGlvbiArIElgKSAKLSBgSW5zZXJ0YCBtZW51IGluIHRoZSBlZGl0b3IgdG9vbGJhci4KCkVhY2ggbGluZSBvZiBSIGNhbiBiZSBhZ2FpbiBleGVjdXRlZCBieSBjbGlja2luZyBvbiB0aGUgbGluZSBhbmQgcHJlc3NpbmcgYEN0cmwgKyBFbnRlcmAgKG1hY09TOiBgQ21kICsgRW50ZXJgKSwgb3IgeW91IGNhbiBwcmVzcyB0aGUgZ3JlZW4gdHJpYW5nbGUgb24gdGhlIHJpZ2h0LWhhbmQgc2lkZSB0byBydW4gZXZlcnl0aGluZyBpbiB0aGUgY2h1bmsuCgojIyAgU3BlY2lmeWluZyBnb29kIGZpbGUgcGF0aHMKCgpPbmUgb2YgdGhlIGJpZ2dlc3Qgc3RyZW5ndGhzIG9mIHdvcmtpbmcgaW4gUHJvamVjdHMgaXMgdGhlIGNvbnZlbnRpb24gb2YgKipzcGVjaWZ5aW5nIGZpbGUgcGF0aHMgcmVsYXRpdmUgdG8gdGhlIHByb2plY3Qgcm9vdCoqLiBJZiBhbGwgZmlsZSBwYXRocyBpbiB0aGUgcHJvamVjdCBhcmUgc3BlY2lmaWVkIHJlbGF0aXZlIHRvIHRoaXMgcm9vdCwgY29kZSBpbnZvbHZpbmcgcGF0aHMgd2lsbCBkaXJlY3QgdG8gdGhlIGNvcnJlY3QgbG9jYXRpb25zLCBldmVuIGlmIHRoZSBQcm9qZWN0IGlzIG1vdmVkLgoKQSBncmVhdCB3YXkgdG8gY3JlYXRlIHJvYnVzdCBmaWxlIHBhdGhzIGlzIGJ5IHVzaW5nIGZ1bmN0aW9uIGBoZXJlKClgIGluIHBhY2thZ2UgYGhlcmVgLiBUaGUgZnVuY3Rpb24gY29uc3RydWN0cyBwYXRocyB0byB5b3VyIHByb2plY3QncyBmaWxlcyByZWxhdGl2ZSB0byBhIHByb2plY3QncyByb290IHRoYXQgd29yayBhY3Jvc3MgZGlmZmVyZW50IG9wZXJhdGluZyBzeXN0ZW1zLgoKTGV0J3MgZmlyc3QgaW5zdGFsbCB0aGUgcGFja2FnZToKCmBgYHtyLCBldmFsID0gRn0KaW5zdGFsbC5wYWNrYWdlcygiaGVyZSIpCmBgYAoKV2UgY2FuIG5vdyB1c2UgZnVuY3Rpb24gYGhlcmUoKWAgdG8gY3JlYXRlIGEgcGF0aCB0byB0aGUgYGdhcG1pbmRlci5jc3ZgIGluIHRoZSBgcmF3X2RhdGEvYCBmb2xkZXIuIFdlIGNhbiBhc3NpZ24gaXQgdG8gYSB2YXJpYWJsZSB3ZSdsbCB1c2UgbmV4dCB0byBsb2FkIHRoZSBmaWxlLgoKYGBge3J9CmdhcG1pbmRlcl9wYXRoIDwtIGhlcmU6OmhlcmUoInJhd19kYXRhIiwgImdhcG1pbmRlci5jc3YiKQoKZ2FwbWluZGVyX3BhdGgKYGBgCgo8ZGl2IGNsYXNzPSJhbGVydCBhbGVydC13YXJuaW5nIj4KCioqUXVlc3Rpb246IFdoeSB3b3VsZCBzcGVjaWZ5aW5nIGBnYXBtaW5kZXJfcGF0aGAgYXMgYCIvVXNlcnMvQW5uYS9Eb2N1bWVudHMvd29ya2Zsb3dzL3dvcmtzaG9wcy9yLWNyYXNoLWNvdXJzZS9yYXdfZGF0YS9nYXBtaW5kZXIuY3N2ImAgYmUgYSBiYWQgaWRlYT8qKgoKPC9kaXY+CgoKIyMgUmVhZGluZyBpbiBkYXRhCgpBbnkgYC5jc3ZgIGZpbGUgY2FuIGJlIGltcG9ydGVkIGludG8gUiBieSBzdXBwbHlpbmcgdGhlIHBhdGggdG8gdGhlIGZpbGUgdG8gYHJlYWRyYCBmdW5jdGlvbiBgcmVhZF9jc3ZgIGFuZCBhc3NpZ25pbmcgaXQgdG8gYSBuZXcgb2JqZWN0IHRvIHN0b3JlIHRoZSByZXN1bHQuIEEgdXNlZnVsIHNhbml0eSBjaGVjayBpcyB0aGUgYGZpbGUuZXhpc3RzYCBmdW5jdGlvbiB3aGljaCB3aWxsIHByaW50IGBUUlVFYCBpcyB0aGUgZmlsZSBjYW4gYmUgZm91bmQgaW4gdGhlIHdvcmtpbmcgZGlyZWN0b3J5LgoKYGBge3J9CmZpbGUuZXhpc3RzKGdhcG1pbmRlcl9wYXRoKQpgYGAKCgpBc3N1bWluZyB0aGUgZmlsZSBjYW4gYmUgZm91bmQsIHdlIGNhbiB1c2UgYHJlYWRfY3N2YCB0byBpbXBvcnQuIE90aGVyIGZ1bmN0aW9ucyBjYW4gYmUgdXNlZCB0byByZWFkIHRhYi1kZWxpbWl0ZWQgZmlsZXMgKGByZWFkX2RlbGltYCkgb3IgYSBnZW5lcmljIGByZWFkLnRhYmxlYCBmdW5jdGlvbi4gQSBkYXRhIGZyYW1lIG9iamVjdCBpcyBjcmVhdGVkLgoKYGBge3J9CmdhcG1pbmRlciA8LSByZWFkX2NzdihnYXBtaW5kZXJfcGF0aCkKYGBgCgpUaGUgZGF0YSBmcmFtZSBvYmplY3QgaW4gUiBhbGxvd3MgdXMgdG8gd29yayB3aXRoICJ0YWJ1bGFyIiBkYXRhLCBsaWtlIHdlIG1pZ2h0IGJlIHVzZWQgdG8gZGVhbGluZyB3aXRoIGluIEV4Y2VsLCB3aGVyZSBvdXIgZGF0YSBjYW4gYmUgdGhvdWdodCBvZiBoYXZpbmcgcm93cyBhbmQgY29sdW1ucy4gVGhlIHZhbHVlcyBpbiBlYWNoIGNvbHVtbiBoYXZlIHRvIGFsbCBiZSBvZiB0aGUgc2FtZSB0eXBlIChpLmUuIGFsbCBudW1iZXJzIG9yIGFsbCB0ZXh0KS4KCkluIFJzdHVkaW8sIHlvdSBjYW4gdmlldyB0aGUgY29udGVudHMgb2YgdGhlIGRhdGEgZnJhbWUgd2UgaGF2ZSBqdXN0IGNyZWF0ZWQuIFRoaXMgaXMgdXNlZnVsIGZvciBpbnRlcmFjdGl2ZSBleHBsb3JhdGlvbiBvZiB0aGUgZGF0YSwgYnV0IG5vdCBzbyB1c2VmdWwgZm9yIGF1dG9tYXRpb24gYW5kIHNjcmlwdGluZyBhbmQgYW5hbHlzZXMuCgpgYGB7ciBldmFsPUZBTFNFfQpWaWV3KGdhcG1pbmRlcikKYGBgCgpgYGB7ciwgZWNobyA9IEZBTFNFfQpnYXBtaW5kZXIKYGBgCgoKCldlIHNob3VsZCBhbHdheXMgY2hlY2sgdGhlIGRhdGEgZnJhbWUgdGhhdCB3ZSBoYXZlIGNyZWF0ZWQuIFNvbWV0aW1lcyBSIHdpbGwgaGFwcGlseSByZWFkIGRhdGEgdXNpbmcgYW4gaW5hcHByb3ByaWF0ZSBmdW5jdGlvbiBhbmQgY3JlYXRlIGFuIG9iamVjdCB3aXRob3V0IHJhaXNpbmcgYW4gZXJyb3IuIEhvd2V2ZXIsIHRoZSBkYXRhIG1pZ2h0IGJlIHVuc3VhYmxlLiBDb25zaWRlcjotCgpgYGB7cn0KdGVzdCA8LSByZWFkX3RhYmxlKGdhcG1pbmRlcl9wYXRoKQpgYGAKCmBgYHtyLCBldmFsPUZ9ClZpZXcodGVzdCkKYGBgCgpgYGB7ciwgZWNobyA9IEZBTFNFfQp0ZXN0CmBgYAoKIyMgTWFuaXB1bGF0aW5nIGRhdGEKCldlIGFyZSBnb2luZyB0byB1c2UgZnVuY3Rpb25zIGZyb20gdGhlIGBkcGx5cmAgcGFja2FnZSwgd2hpY2ggaXMgYXV0b21hdGljYWxseSBsb2FkZWQgYnkgbG9hZGluZyB0aGUgYHRpZHl2ZXJzZWAgdG8gbWFuaXB1bGF0ZSB0aGUgZGF0YSBmcmFtZSB3ZSBoYXZlIGp1c3QgY3JlYXRlZC4gSXQgaXMgcGVyZmVjdGx5IHBvc3NpYmxlIHRvIHdvcmsgd2l0aCBkYXRhIGZyYW1lIHVzaW5nIHRoZSBmdW5jdGlvbnMgcHJvdmlkZWQgYXMgcGFydCBvZiAiKmJhc2UgUioiLiBIb3dldmVyLCBtYW55IGZpbmQgaXQgZWFzeSB0byByZWFkIGFuZCB3cml0ZSBjb2RlIHVzaW5nIGBkcGx5cmAuCgpUaGVyZSBhcmUgbWFueSBtb3JlIGZ1bmN0aW9ucyBhdmFpbGFibGUgaW4gYGRwbHlyYCB0aGFuIHdlIHdpbGwgY292ZXIgdG9kYXkuIEFuIG92ZXJ2aWV3IG9mIGFsbCBmdW5jdGlvbnMgaXMgZ2l2ZW4gaW4gdGhlIGZvbGxvd2luZyBbY2hlYXRzaGVldF0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTUvMDIvZGF0YS13cmFuZ2xpbmctY2hlYXRzaGVldC5wZGYpCgojIyMgYHNlbGVjdGBpbmcgY29sdW1ucwoKCldlIGNhbiBhY2Nlc3MgdGhlIGNvbHVtbnMgb2YgYSBkYXRhIGZyYW1lIGJ5IGtub3dpbmcgdGhlIGNvbHVtbiBuYW1lIHVzaW5nIHRoZSBgc2VsZWN0YCBmdW5jdGlvbi4gVGhlIGNvbHVtbiBuYW1lcyB0aGF0IHdlIHdhbnQgdG8gZGlzcGxheSBhcmUgbGlzdGVkIGFmdGVyIHRoZSBuYW1lIG9mIHRoZSBkYXRhIGZyYW1lLCBzZXBhcmF0ZWQgYnkgYSBgLGAgLiAKCmBgYHtyfQpzZWxlY3QoZ2FwbWluZGVyLCBjb3VudHJ5LGNvbnRpbmVudCkKYGBgCgpXZSBjYW4gYWxzbyBzdXBwcmVzcyBzb21lIGNvbHVtbnMgZnJvbSBhcHBlYXJpbmcgaW4gdGhlIG91dHB1dCBieSBwdXR0aW5nIGEgYC1gIGluIGZyb250IG9mIHRoZSBjb2x1bW4gbmFtZS4KCmBgYHtyfQpzZWxlY3QoZ2FwbWluZGVyLCAtY291bnRyeSkKYGBgCgpBIHJhbmdlIG9mIGNvbHVtbnMgY2FuIGJlIHNlbGVjdGVkIGJ5IHRoZSBgOmAgb3BlcmF0b3IuCgpgYGB7cn0Kc2VsZWN0KGdhcG1pbmRlciwgbGlmZUV4cDpnZHBQZXJjYXApCmBgYAoKVGhlcmUgYXJlIGEgbnVtYmVyIG9mIGhlbHBlciBmdW5jdGlvbnMgY2FuIGJlIGVtcGxveWVkIGlmIHdlIGFyZSB1bnN1cmUgYWJvdXQgdGhlIGV4YWN0IG5hbWUgb2YgdGhlIGNvbHVtbi4KCmBgYHtyfQpzZWxlY3QoZ2FwbWluZGVyLCBzdGFydHNfd2l0aCgibGlmZSIpKQpzZWxlY3QoZ2FwbWluZGVyLCBjb250YWlucygicG9wIikpCmBgYAoKIyMjIFJlc3RyaWN0aW5nIHJvd3Mgd2l0aCBmaWx0ZXIKClNvIGZhciB3ZSBoYXZlIGJlZW4gcmV0dXJuaW5nIGFsbCB0aGUgcm93cyBpbiB0aGUgb3V0cHV0LiBXZSBjYW4gdXNlIHdoYXQgd2UgY2FsbCBhICpsb2dpY2FsIHRlc3QqIHRvIGRlZmluZSB3aGF0IHJvd3MgYXJlIGRpc3BsYXllZCBpbiB0aGUgb3V0cHV0LiBUaGlzIGlzIGEgdGVzdCB0aGF0IGdpdmVzIGVpdGhlciBhIGBUUlVFYCBvciBgRkFMU0VgIHJlc3VsdC4gV2hlbiBhcHBsaWVkIHRvIHN1YnNldHRpbmcsIG9ubHkgcm93cyB3aXRoIGEgYFRSVUVgIHJlc3VsdCBnZXQgcmV0dXJuZWQuCgpGb3IgZXhhbXBsZSB3ZSBjb3VsZCBjb21wYXJlIHRoZSBgbGlmZUV4cGAgdmFyaWFibGUgdG8gNDAuIEludGVybmFsbHksIFIgY3JlYXRlcyBhICp2ZWN0b3IqIG9mIGBUUlVFYCBvciBgRkFMU0VgOyBvbmUgZm9yIGVhY2ggcm93IGluIHRoZSBkYXRhIGZyYW1lLiBUaGlzIGlzIHRoZW4gdXNlZCB0byBkZWNpZGUgd2hpY2ggcm93cyB0byBkaXNwbGF5LgoKYGBge3J9CmZpbHRlcihnYXBtaW5kZXIsIGxpZmVFeHAgPCA0MCkKYGBgCgoKVGVzdGluZyBmb3IgZXF1YWxpdHkgY2FuIGJlIGRvbmUgdXNpbmcgYD09YC4gVGhpcyB3aWxsIG9ubHkgZ2l2ZSBgVFJVRWAgZm9yIGVudHJpZXMgdGhhdCBhcmUgKmV4YWN0bHkqIHRoZSBzYW1lIGFzIHRoZSB0ZXN0IHN0cmluZy4gCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgY291bnRyeSA9PSAiWmFtYmlhIikKCmBgYAoKTi5CLiBGb3IgcGFydGlhbCBtYXRjaGVzLCB0aGUgYGdyZXBgIGZ1bmN0aW9uIGFuZCAvIG9yICpyZWd1bGFyIGV4cHJlc3Npb25zKiAoaWYgeW91IGtub3cgdGhlbSkgY2FuIGJlIHVzZWQuCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgZ3JlcGwoImxhbmQiLCBjb3VudHJ5KSkKYGBgCgoKVGhlcmUgYXJlIGEgY291cGxlIG9mIHdheXMgb2YgdGVzdGluZyBmb3IgbW9yZSB0aGFuIG9uZSB0ZXh0IHZhbHVlLiBUaGUgZmlyc3QgdXNlcyBhbiAqb3IqIGB8YCBzdGF0ZW1lbnQuIGkuZS4gdGVzdGluZyBpZiB0aGUgdmFsdWUgb2YgYGNvdW50cnlgIGlzIGBaYW1iaWFgICpvciogdGhlIHZhbHVlIGlzIGBaaW1iYWJ3ZWAuIFJlbWVtYmVyIHRvIHVzZSBkb3VibGUgYD1gIHNpZ24gdG8gdGVzdCBmb3Igc3RyaW5nIGVxdWFsaXR5OyBgPT1gLgoKVGhlIGAlaW4lYCBmdW5jdGlvbiBpcyBhIGNvbnZlbmllbnQgZnVuY3Rpb24gZm9yIHRlc3Rpbmcgd2hpY2ggaXRlbXMgaW4gYSB2ZWN0b3IgY29ycmVzcG9uZCB0byBhIGRlZmluZWQgc2V0IG9mIHZhbHVlcy4KCldlIGNhbiByZXF1aXJlIHRoYXQgYm90aCB0ZXN0cyBhcmUgYFRSVUVgIGJ5IHVzaW5nIGFuICphbmQqIGAmYCBvcGVyYXRpb24uIGUuZy4gd2hpY2ggeWVhcnMgaW4gWmFtYmlhIGhhZCBhIGxpZmUgZXhwZWN0YW5jeSBsZXNzIHRoYW4gNDAuCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgY291bnRyeSA9PSAiWmFtYmlhIiAmIGxpZmVFeHAgPCA0MCkKZmlsdGVyKGdhcG1pbmRlciwgY291bnRyeSA9PSAiWmFtYmlhIiwgbGlmZUV4cCA8IDQwKQoKYGBgCgpUbyBhbGxvdyAKCmBgYHtyfQpmaWx0ZXIoZ2FwbWluZGVyLCBjb3VudHJ5ID09ICJaYW1iaWEiIHwgY291bnRyeSA9PSAiWmltYmFid2UiKQpgYGAKCkZpbmFsbHksIHdlIGhhdmUgYCE9YCBmb3IgdGVzdGluZyBpZiBzb21ldGhpbmcgaXMgKm5vdCogZXF1YWwKCmBgYHtyfQpmaWx0ZXIoZ2FwbWluZGVyLCBjb250aW5lbnQgIT0gIkV1cm9wZSIpCgpgYGAKCgoqKioqKioKKioqKioqCioqKioqKgoKIyMjIyBFeGVyY2lzZQoKLSBDcmVhdGUgYSBzdWJzZXQgb2YgdGhlIGRhdGEgd2hlcmUgdGhlIHBvcHVsYXRpb24gbGVzcyB0aGFuIGEgbWlsbGlvbiBpbiB0aGUgeWVhciAyMDAyCi0gQ3JlYXRlIGEgc3Vic2V0IG9mIHRoZSBkYXRhIHdoZXJlIHRoZSBsaWZlIGV4cGVjdGFuY3kgaXMgZ3JlYXRlciB0aGFuIDgwIGluIHRoZSB5ZWFyIDIwMDIKLSBDcmVhdGUgYSBzdWJzZXQgb2YgdGhlICoqRXVyb3BlYW4qKiBkYXRhIHdoZXJlIHRoZSBsaWZlIGV4cGVjdGFuY3kgaXMgZ3JlYXRlciB0aGFuIDgwIGluIGVpdGhlciB0aGUgeWVhciAyMDAyICpvciogMjAwNwoKYGBge3J9CgpgYGAKCgoKKioqKioqCioqKioqKgoqKioqKioKCgpBcyB3ZWxsIGFzIHNlbGVjdGluZyBleGlzdGluZyBjb2x1bW5zIGluIHRoZSBkYXRhIGZyYW1lLCBuZXcgY29sdW1ucyBjcmVhdGVkIHVzaW5nIHRoZSBgbXV0YXRlYCBmdW5jdGlvbi4gV2Ugd291bGQgdHlwaWNhbGx5IHVzZSBhIGZ1bmN0aW9uIHRoYXQgd291bGQgdGFrZSBhbiBleGlzdGluZyBjb2x1bW4gYW5kIGFwcGx5IHNvbWUgb3BlcmF0aW9uIHRvIGVhY2ggZW50cnkgaW4gdGhlIGNvbHVtbiBpbi10dXJuLiBJbiBvdGhlciB3b3JkcywgdGhlIG51bWJlciBvZiB2YWx1ZXMgcmV0dXJuZWQgYnkgdGhlIGZ1bmN0aW9uIG11c3QgYmUgdGhlIHNhbWUgYXMgdGhlIG51bWJlciBvZiBpbnB1dCB2YWx1ZXMuCgpgYGB7cn0KZ2FwbWluZGVyX2V4dHJhIDwtIG11dGF0ZShnYXBtaW5kZXIsIFBvcEluTWlsbGlvbnMgPSBwb3AgLyAxZTYpCmdhcG1pbmRlcl9leHRyYSA8LSBtdXRhdGUoZ2FwbWluZGVyX2V4dHJhLCBsaWZlRXhwID0gcm91bmQobGlmZUV4cCkpCmdhcG1pbmRlcl9leHRyYQpgYGAKCiMjIyBPcmRlcmluZyBhbmQgc29ydGluZwoKVGhlIHdob2xlIGRhdGEgZnJhbWUgY2FuIGJlIHJlLW9yZGVyZWQgYWNjb3JkaW5nIHRvIHRoZSB2YWx1ZXMgaW4gb25lIGNvbHVtbiB1c2luZyB0aGUgYGFycmFuZ2VgIGZ1bmN0aW9uLiBTbyB0byBvcmRlciB0aGUgdGFibGUgYWNjb3JkaW5nIHRvIHBvcHVsYXRpb24gc2l6ZTotCgpgYGB7cn0KYXJyYW5nZShnYXBtaW5kZXIscG9wKQogIApgYGAKClRoZSBkZWZhdWx0IGlzIGBzbWFsbGVzdCAtLT4gbGFyZ2VzdGAgYnkgd2UgY2FuIGNoYW5nZSB0aGlzIHVzaW5nIHRoZSBgZGVzY2AgZnVuY3Rpb24KCmBgYHtyfQphcnJhbmdlKGdhcG1pbmRlcixkZXNjKHBvcCkpCmBgYAoKYGFycmFuZ2VgIGFsc28gd29ya3Mgb24gY2hhcmFjdGVyIHZlY3RvcnMKCmBgYHtyfQphcnJhbmdlKGdhcG1pbmRlciwgZGVzYyhjb3VudHJ5KSkKYGBgCgpXZSBjYW4gZXZlbiBvcmRlciBieSBtb3JlIHRoYW4gb25lIGNvbmRpdGlvbgoKYGBge3IgZXZhbD1GQUxTRX0KYXJyYW5nZShnYXBtaW5kZXIsIHllYXIscG9wKQpgYGAKCgpBIGZpbmFsIHBvaW50IG9uIGRhdGEgZnJhbWVzIGlzIHRoYXQgd2UgY2FuIGV4cG9ydCB0aGVtIG91dCBvZiBSIG9uY2Ugd2UgaGF2ZSBkb25lIG91ciBkYXRhIHByb2Nlc3NpbmcuIAoKTGV0J3MgY3JlYXRlIGEgZm9sZGVyIGluIHdoaWNoIHRvIHN0b3JlIHN1Y2ggcHJvY2Vzc2VkLCBhbmFseXNpcyByZWFkeSBkYXRhCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZGlyLmNyZWF0ZSgiZGF0YSIpCmBgYAoKCmBgYHtyfQpieVdlYWx0aCA8LSBhcnJhbmdlKGdhcG1pbmRlciwgZGVzYyhnZHBQZXJjYXApKQpoZWFkKGJ5V2VhbHRoKQp3cml0ZV9jc3YoYnlXZWFsdGgsIHBhdGggPSBoZXJlOjpoZXJlKCJkYXRhIiwgImRhdGFPcmRlcmVkQnlXZWFsdGguY3N2IikpCmBgYAoKV2Ugd2lsbCBub3cgdHJ5IGFuIGV4ZXJjaXNlIHRoYXQgaW52b2x2ZXMgdXNpbmcgc2V2ZXJhbCBzdGVwcyBvZiB0aGVzZSBvcGVyYXRpb25zCgoqKioqKioKKioqKioqCioqKioqKgoKIyMjIyBFeGVyY2lzZQoKLSBGaWx0ZXIgdGhlIGRhdGEgdG8gaW5jbHVkZSBqdXN0IG9ic2VydmF0aW9ucyBmcm9tIHRoZSB5ZWFyIDIwMDIKLSBPcmRlciB0aGUgdGFibGUgYnkgaW5jcmVhc2luZyBsaWZlIGV4cGVjdGFuY3kKLSBSZW1vdmUgdGhlIHllYXIgY29sdW1uIGZyb20gdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lCi0gV3JpdGUgdGhlIGRhdGEgZnJhbWUgb3V0IHRvIGEgZmlsZQoKYGBge3J9CgoKCmBgYAoKCioqKioqKgoqKioqKioKKioqKioqCgoKIyAiUGlwaW5nIgoKV2Ugd2lsbCBvZnRlbiBuZWVkIHRvIHBlcmZvcm0gYW4gYW5hbHlzaXMsIG9yIGNsZWFuIGEgZGF0YXNldCwgdXNpbmcgc2V2ZXJhbCBgZHBseXJgIGZ1bmN0aW9ucyBpbiBzZXF1ZW5jZS4gZS5nLiBmaWx0ZXJpbmcsIG11dGF0aW5nLCB0aGVuIHNlbGVjdGluZyBjb2x1bW5zIG9mIGludGVyZXN0IChwb3NzaWJseSBmb2xsb3dlZCBieSBwbG90dGluZyAtIHNlZSBsYXRlcikuCgpJZiB3ZSB3YW50ZWQgdG8gZmlsdGVyIG91ciByZXN1bHRzIHRvIGp1c3QgRXVyb3BlIHRoZXJlIGlzIG5vIHBvaW50IGRpc3BsYXlpbmcgdGhlIGNvbnRpbmVudCBjb2x1bW4gaW4gb3VyIG91dHB1dCwgc28gd2UgZG9uJ3QgbmVlZCB0byBzaG93IGl0LgoKVGhlIGZvbGxvd2luZyBpcyBwZXJmZWN0bHkgdmFsaWQgUiBjb2RlLCBidXQgaW52aXRlcyB0aGUgdXNlciB0byBtYWtlIG1pc3Rha2VzIHdoZW4gd3JpdGluZyBpdC4gV2UgYWxzbyBoYXZlIHRvIGNyZWF0ZSBtdWx0aXBsZSBjb3BpZXMgb2YgdGhlIHNhbWUgZGF0YSBmcmFtZS4KCmBgYHtyfQp0bXAgPC0gZmlsdGVyKGdhcG1pbmRlciwgY29udGluZW50ID09ICJFdXJvcGUiKQp0bXAyIDwtIHNlbGVjdCh0bXAsIC1jb250aW5lbnQpCmBgYAoKVGhvc2UgZmFtaWxpYXIgd2l0aCBVbml4IG1heSByZWNhbGwgdGhhdCBjb21tYW5kcyBjYW4gYmUgam9pbmVkIHdpdGggYSBwaXBlOyBgfGAKCkluIFIsIGBkcGx5cmAgY29tbWFuZHMgdG8gYmUgbGlua2VkIHRvZ2V0aGVyIGFuZCBmb3JtIGEgd29ya2Zsb3cuIFRoZSBzeW1ib2wgYCU+JWAgaXMgcHJvbm91bmNlZCAqKnRoZW4qKi4gV2l0aCBhIGAlPiUgYCB0aGUgaW5wdXQgdG8gYSBmdW5jdGlvbiBpcyBhc3N1bWVkIHRvIGJlIHRoZSBvdXRwdXQgb2YgdGhlIHByZXZpb3VzIGxpbmUuIEFsbCB0aGUgYGRwbHlyYCBmdW5jdGlvbnMgdGhhdCB3ZSBoYXZlIHNlZW4gc28gZmFyIHRha2UgYSBkYXRhIGZyYW1lIGFzIGFuIGlucHV0IGFuZCByZXR1cm4gYW4gYWx0ZXJlZCBkYXRhIGZyYW1lIGFzIGFuIG91dHB1dCwgc28gYXJlIGFtZWFuYWJsZSB0byB0aGlzIHR5cGUgb2YgcHJvZ3JhbW1pbmcuCgpUaGUgZXhhbXBsZSB3ZSBnYXZlIG9mIGZpbHRlcmluZyBqdXN0IHRoZSBFdXJvcGVhbiBjb3VudHJpZXMgYW5kIHJlbW92aW5nIHRoZSBgY29udGluZW50YCBjb2x1bW4gYmVjb21lczotCgoqbm90aWNlIHRoYXQgaW4gdGhlIGBzZWxlY3RgIHN0YXRlbWVudCB3ZSBkb24ndCBuZWVkIHRvIHNwZWNpZnkgdGhlIG5hbWUgb2YgdGhlIGRhdGEgZnJhbWUqCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgY29udGluZW50PT0iRXVyb3BlIikgJT4lIAogIHNlbGVjdCgtY29udGluZW50KQoKYGBgCgpXZSBjYW4gam9pbiBhcyBtYW55IGBkcGx5cmAgZnVuY3Rpb25zIGFzIHdlIHJlcXVpcmUgZm9yIHRoZSBhbmFseXNpcy4KCmBgYHtyfQpmaWx0ZXIoZ2FwbWluZGVyLCBjb250aW5lbnQ9PSJFdXJvcGUiKSAlPiUgCiAgc2VsZWN0KC1jb250aW5lbnQpICU+JSAKICBtdXRhdGUobGlmZUV4cCA9IHJvdW5kKGxpZmVFeHApKSAlPiUgCiAgYXJyYW5nZSh5ZWFyLGxpZmVFeHApICU+JSAKICBzZWxlY3QoY291bnRyeSwgeWVhcjpsaWZlRXhwKSAlPiUgCiAgd3JpdGVfY3N2KHBhdGggPSBoZXJlOjpoZXJlKCJkYXRhIiwgImV1cm9wZS1ieS1saWZlLWV4cC5jc3YiKSkKCmBgYAoKCiMgUGxvdHRpbmcKClRoZSBSIGxhbmd1YWdlIGhhcyBleHRlbnNpdmUgZ3JhcGhpY2FsIGNhcGFiaWxpdGllcy4KCkdyYXBoaWNzIGluIFIgbWF5IGJlIGNyZWF0ZWQgYnkgbWFueSBkaWZmZXJlbnQgbWV0aG9kcyBpbmNsdWRpbmcgYmFzZSBncmFwaGljcyBhbmQgbW9yZSBhZHZhbmNlZCBwbG90dGluZyBwYWNrYWdlcyBzdWNoIGFzIGxhdHRpY2UuCgpUaGUgYGdncGxvdDJgIHBhY2thZ2Ugd2FzIGNyZWF0ZWQgYnkgSGFkbGV5IFdpY2toYW0gYW5kIHByb3ZpZGVzIGEgaW50dWl0aXZlIHBsb3R0aW5nIHN5c3RlbSB0byByYXBpZGx5IGdlbmVyYXRlIHB1YmxpY2F0aW9uIHF1YWxpdHkgZ3JhcGhpY3MuCgpgZ2dwbG90MmAgYnVpbGRzIG9uIHRoZSBjb25jZXB0IG9mIHRoZSDigJxHcmFtbWFyIG9mIEdyYXBoaWNz4oCdIChXaWxraW5zb24gMjAwNSwgQmVydGluIDE5ODMpIHdoaWNoIGRlc2NyaWJlcyBhIGNvbnNpc3RlbnQgc3ludGF4IGZvciB0aGUgY29uc3RydWN0aW9uIG9mIGEgd2lkZSByYW5nZSBvZiBjb21wbGV4IGdyYXBoaWNzIGJ5IGEgY29uY2lzZSBkZXNjcmlwdGlvbiBvZiB0aGVpciBjb21wb25lbnRzLgoKIyMgV2h5IHVzZSBnZ3Bsb3QyPwoKVGhlIHN0cnVjdHVyZWQgc3ludGF4IGFuZCBoaWdoIGxldmVsIG9mIGFic3RyYWN0aW9uIHVzZWQgYnkgZ2dwbG90MiBzaG91bGQgYWxsb3cgZm9yIHRoZSB1c2VyIHRvIGNvbmNlbnRyYXRlIG9uIHRoZSB2aXN1YWxpc2F0aW9ucyBpbnN0ZWFkIG9mIGNyZWF0aW5nIHRoZSB1bmRlcmx5aW5nIGNvZGUuCgpPbiB0b3Agb2YgdGhpcyBjZW50cmFsIHBoaWxvc29waHkgZ2dwbG90MiBoYXM6CgotIEluY3JlYXNlZCBmbGV4aWJpbGl0eSBvdmVyIG1hbnkgcGxvdHRpbmcgc3lzdGVtcy4KLSBBbiBhZHZhbmNlZCB0aGVtZSBzeXN0ZW0gZm9yIHByb2Zlc3Npb25hbC9wdWJsaWNhdGlvbiBsZXZlbCBncmFwaGljcy4KLSBMYXJnZSBkZXZlbG9wZXIgYmFzZSDigJMgTWFueSBsaWJyYXJpZXMgZXh0ZW5kaW5nIGl0cyBmbGV4aWJpbGl0eS4KLSBMYXJnZSB1c2VyIGJhc2Ug4oCTIEdyZWF0IGRvY3VtZW50YXRpb24gYW5kIGFjdGl2ZSBtYWlsaW5nIGxpc3QuCgoKSXQgaXMgYWx3YXlzIHVzZWZ1bCB0byB0aGluayBhYm91dCB0aGUgbWVzc2FnZSB5b3Ugd2FudCB0byBjb252ZXkgYW5kIHRoZSBhcHByb3ByaWF0ZSBwbG90IGJlZm9yZSB3cml0aW5nIGFueSBSIGNvZGUuIFJlc291cmNlcyBsaWtlIFt0aGlzXShodHRwczovL3d3dy5kYXRhLXRvLXZpei5jb20vKSBzaG91bGQgaGVscC4KCldpdGggc29tZSBwcmFjdGljZSwgYGdncGxvdDJgIG1ha2VzIGl0IGVhc2llciB0byBnbyBmcm9tIHRoZSBmaWd1cmUgeW91IGFyZSBpbWFnaW5pbmcgaW4gb3VyIGhlYWQgKG9yIG9uIHBhcGVyKSB0byBhIHB1YmxpY2F0aW9uLXJlYWR5IGltYWdlIGluIFIuCgpBcyB3aXRoIGBkcGx5cmAsIHdlIHdvbid0IGhhdmUgdGltZSB0byBjb3ZlciBhbGwgZGV0YWlscyBvZiBgZ2dwbG90MmAuIFRoaXMgaXMgaG93ZXZlciBhIHVzZWZ1bCBbY2hlYXRzaGVldF0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTUvMDMvZ2dwbG90Mi1jaGVhdHNoZWV0LnBkZikgdGhhdCBjYW4gYmUgcHJpbnRlZCBhcyBhIHJlZmVyZW5jZS4KCiMjIEJhc2ljIHBsb3QgdHlwZXMKCkEgcGxvdCBpbiBgZ2dwbG90MmAgaXMgY3JlYXRlZCB3aXRoIHRoZSBmb2xsb3dpbmcgdHlwZSBvZiBjb21tYW5kCgpgYGAKZ2dwbG90KGRhdGEgPSA8REFUQT4sIG1hcHBpbmcgPSBhZXMoPE1BUFBJTkdTPikpICsgIDxHRU9NX0ZVTkNUSU9OPigpCmBgYAoKU28gd2UgbmVlZCB0byBzcGVjaWZ5CgotIFRoZSBkYXRhIHRvIGJlIHVzZWQgaW4gZ3JhcGgKLSBNYXBwaW5ncyBvZiBkYXRhIHRvIHRoZSBncmFwaCAoKmFlc3RoZXRpYyogbWFwcGluZykKLSBXaGF0IHR5cGUgb2YgZ3JhcGggd2Ugd2FudCB0byB1c2UgKFRoZSAqZ2VvbSogdG8gdXNlKS4KCkxldHMgc2F5IHRoYXQgd2Ugd2FudCB0byBleHBsb3JlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBHRFAgYW5kIExpZmUgRXhwZWN0YW5jeS4gV2UgbWlnaHQgc3RhcnQgd2l0aCB0aGUgaHlwb3RoZXNpcyB0aGF0IHJpY2hlciBjb3VudHJpZXMgaGF2ZSBoaWdoZXIgbGlmZSBleHBlY3RhbmN5LiBBIHNlbnNpYmxlIGNob2ljZSBvZiBwbG90IHdvdWxkIGJlIGEgKnNjYXR0ZXIgcGxvdCogd2l0aCBnZHAgb24gdGhlIHgtYXhpcyBhbmQgbGlmZSBleHBlY3RhbmN5IG9uIHRoZSB5LWF4aXMuCgpUaGUgZmlyc3Qgc3RhZ2UgaXMgdG8gc3BlY2lmeSBvdXIgZGF0YXNldAoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXIpCmBgYAoKRm9yIHRoZSBhZXN0aGV0aWNzLCBhcyBhIGJhcmUgbWluaW11bSB3ZSB3aWxsIG1hcCB0aGUgYGdkcFBlcmNhcGAgYW5kIGBsaWZlRXhwYCB0byB0aGUgeC0gYW5kIHktYXhpcyBvZiB0aGUgcGxvdAoKYGBge3J9CmdncGxvdChkYXRhID0gZ2FwbWluZGVyLGFlcyh4PWdkcFBlcmNhcCwgeT1saWZlRXhwKSkKYGBgCgpUaGF0IGNyZWF0ZWQgdGhlIGF4ZXMsIGJ1dCB3ZSBzdGlsbCBuZWVkIHRvIGRlZmluZSBob3cgdG8gZGlzcGxheSBvdXIgcG9pbnRzIG9uIHRoZSBwbG90LiBBcyB3ZSBoYXZlIGNvbnRpbnVvdXMgZGF0YSBmb3IgYm90aCB0aGUgeC0gYW5kIHktYXhpcywgYGdlb21fcG9pbnRgIGlzIGEgZ29vZCBjaG9pY2UuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXIsYWVzKHg9Z2RwUGVyY2FwLCB5PWxpZmVFeHApKSArIGdlb21fcG9pbnQoKQpgYGAKCgoKClRoZSAqZ2VvbSogd2UgdXNlIHdpbGwgZGVwZW5kIG9uIHdoYXQga2luZCBvZiBkYXRhIHdlIGhhdmUgKGNvbnRpbnVvdXMsIGNhdGVnb3JpY2FsIGV0YykKCi0gYGdlb21fcG9pbnQoKWAgLSBTY2F0dGVyIHBsb3RzCi0gYGdlb21fbGluZSgpYCAtIExpbmUgcGxvdHMKLSBgZ2VvbV9zbW9vdGgoKWAgLSBGaXR0ZWQgbGluZSBwbG90cwotIGBnZW9tX2JhcigpYCAtIEJhciBwbG90cwotIGBnZW9tX2JveHBsb3QoKWAgLSBCb3hwbG90cwotIGBnZW9tX2ppdHRlcigpYCAtIEppdHRlciB0byBwbG90cwotIGBnZW9tX2hpc3RvZ3JhbSgpYCAtIEhpc3RvZ3JhbSBwbG90cwotIGBnZW9tX2RlbnNpdHkoKWAgLSBEZW5zaXR5IHBsb3RzCi0gYGdlb21fdGV4dCgpYCAtIFRleHQgdG8gcGxvdHMKLSBgZ2VvbV9lcnJvcmJhcigpYCAtIEVycm9yYmFycyB0byBwbG90cwotIGBnZW9tX3Zpb2xpbigpYCAtIFZpb2xpbiBwbG90cwoKCkJveHBsb3RzIGFyZSBjb21tb25seSB1c2VkIHRvIHZpc3VhbGlzZSB0aGUgZGlzdHJpYnV0aW9ucyBvZiBjb250aW51b3VzIGRhdGEuIFdlIGhhdmUgdG8gdXNlIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgb24gdGhlIHgtYXhpcy4gSW4gdGhlIGNhc2Ugb2YgdGhlIGBnYXBtaW5kZXJgIGRhdGEgd2UgbWlnaHQgaGF2ZSB0byBwZXJzdWFkZSBgZ2dwbG90MmAgdGhhdCB0aGUgYHllYXJgIGNvbHVtbiBpcyBhIGBmYWN0b3JgIHJhdGhlciB0aGFuIG51bWVyaWNhbCBkYXRhLgoKYGBge3J9CmdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0gYXMuZmFjdG9yKHllYXIpLCB5PWdkcFBlcmNhcCkpICsgZ2VvbV9ib3hwbG90KCkKYGBgCgoKYGBge3J9CmdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0gZ2RwUGVyY2FwKSkgKyBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKQ291bnRzIHdpdGggYSBiYXJwbG90CgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHg9Y29udGluZW50KSkgKyBnZW9tX2JhcigpCmBgYAoKCgoqKioqKioKKioqKioqCioqKioqKgoKIyMjIEV4ZXJjaXNlCgotIENyZWF0ZSBhIHN1YnNldCBvZiB0aGUgYGdhcG1pbmRlcmAgZGF0YSBmcmFtZSBjb250YWluaW5nIGp1c3QgdGhlIHJvd3MgZm9yIHlvdXIgY291bnRyeSBvZiBiaXJ0aAotIEhhcyB0aGVyZSBiZWVuIGFuIGluY3JlYXNlIGluIGxpZmUgZXhwZWN0YW5jeSBvdmVyIHRpbWU/CiAgICArIHZpc3VhbGlzZSB0aGUgdHJlbmQgdXNpbmcgYSBzY2F0dGVyIHBsb3QgKGBnZW9tX3BvaW50YCksIGxpbmUgZ3JhcGggKGBnZW9tX2xpbmVgKSBvciBzbW9vdGhlZCBsaW5lIChgZ2VvbV9zbW9vdGhgKS4KCioqKioqKgoqKioqKioKKioqKioqCgoKIyMgQ3VzdG9taXNpbmcgdGhlIHBsb3QgYXBwZWFyYW5jZQoKT3VyIHBsb3RzIGFyZSBhIGJpdCBkcmVhcnkgYXQgdGhlIG1vbWVudCwgYnV0IG9uZSB3YXkgdG8gYWRkIGNvbG91ciBpcyB0byBhZGQgYSBgY29sYCBhcmd1bWVudCB0byB0aGUgYGdlb21fcG9pbnRgIGZ1bmN0aW9uLiBUaGUgdmFsdWUgY2FuIGJlIGFueSBvZiB0aGUgcHJlLWRlZmluZWQgY29sb3VyIG5hbWVzIGluIFIuIFRoZXNlIGFyZSBkaXNwbGF5ZWQgaW4gdGhpcyBbaGFuZHkgb25saW5lIHJlZmVyZW5jZV0oaHR0cDovL3d3dy5zdGF0LmNvbHVtYmlhLmVkdS9+dHpoZW5nL2ZpbGVzL1Jjb2xvci5wZGYpLiAqUiplZCwgKkcqcmVlbiwgKkIqbHVlIG9mICpIZXgqIHZhbHVlcyBjYW4gYWxzbyBiZSBnaXZlbi4KCmBgYHtyfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGdkcFBlcmNhcCwgeT1saWZlRXhwKSkgKyBnZW9tX3BvaW50KGNvbD0icmVkIikKYGBgCgpIb3dldmVyLCBhIHBvd2VyZnVsIGZlYXR1cmUgb2YgYGdncGxvdDJgIGlzIHRoYXQgY29sb3VycyBhcmUgdHJlYXRlZCBhcyBhZXN0aGV0aWNzIG9mIHRoZSBwbG90LiBJbiBvdGhlciB3b3JkcyB3ZSBjYW4gdXNlIGNvbHVtbiBpbiBvdXIgZGF0YXNldC4KCkxldCdzIHNheSB0aGF0IHdlIHdhbnQgcG9pbnRzIG9uIG91ciBwbG90IHRvIGJlIGNvbG91cmVkIGFjY29yZGluZyB0byBjb250aW5lbnQuIFdlIGFkZCBhbiBleHRyYSBhcmd1bWVudCB0byB0aGUgZGVmaW5pdGlvbiBvZiBhZXN0aGV0aWNzIHRvIGRlZmluZSB0aGUgbWFwcGluZy4gYGdncGxvdDJgIHdpbGwgZXZlbiBkZWNpZGUgb24gY29sb3VycyBhbmQgY3JlYXRlIGEgbGVnZW5kIGZvciB1cy4KCmBgYHtyfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGdkcFBlcmNhcCwgeT1saWZlRXhwLGNvbD1jb250aW5lbnQpKSArIGdlb21fcG9pbnQoKQpgYGAKCgoKCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSB5ZWFyLCB5PWdkcFBlcmNhcCxmaWxsPWFzLmZhY3Rvcih5ZWFyKSkpICsgZ2VvbV9ib3hwbG90KCkKYGBgCgpTaGFwZSBhbmQgc2l6ZSBvZiBwb2ludHMgY2FuIGFsc28gYmUgbWFwcGVkIGZyb20gdGhlIGRhdGEuIEhvd2V2ZXIsIGl0IGlzIGVhc3kgdG8gZ2V0IGNhcnJpZWQgYXdheS4KCmBgYHtyfQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeCA9IGdkcFBlcmNhcCwgeT1saWZlRXhwLHNoYXBlPWNvbnRpbmVudCxzaXplPXBvcCxjb2w9YXMuZmFjdG9yKHllYXIpKSkgKyBnZW9tX3BvaW50KCkKYGBgCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBnZHBQZXJjYXAsIHk9bGlmZUV4cCxjb2w9Y29udGluZW50KSkgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikKYGBgCgoKIyMgRmFjZXRzCgpPbmUgdmVyeSB1c2VmdWwgZmVhdHVyZSBvZiBnZ3Bsb3QgaXMgZmFjZXRpbmcuIFRoaXMgYWxsb3dzIHlvdSB0byBwcm9kdWNlIHBsb3RzIHN1YnNldCBieSB2YXJpYWJsZXMgaW4geW91ciBkYXRhLiBJbiB0aGUgc2NhdHRlciBwbG90IGFib3ZlLCBpdCB3YXMgcXVpdGUgZGlmZmljdWx0IHRvIHNlZSBpZiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZ2RwIGFuZCBsaWZlIGV4cGVjdGFuY3kgd2FzIHRoZSBzYW1lIGZvciBlYWNoIGNvbnRpbmVudC4gVG8gb3ZlcmNvbWUgdGhpcywgd2Ugd291bGQgbGlrZSBhIHNlZSBhIHNlcGFyYXRlIHBsb3QgZm9yIGVhY2ggY29udGluZW50LgoKVG8gZmFjZXQgb3VyIGRhdGEgaW50byBtdWx0aXBsZSBwbG90cyB3ZSBjYW4gdXNlIHRoZSBgZmFjZXRfd3JhcGAgb3IgYGZhY2V0X2dyaWRgIGZ1bmN0aW9uIGFuZCBzcGVjaWZ5IHRoZSB2YXJpYWJsZSB3ZSBzcGxpdCBieS4gCgpgYGB7cn0KZ2dwbG90KGdhcG1pbmRlciwgYWVzKHggPSBnZHBQZXJjYXAsIHk9bGlmZUV4cCxjb2w9Y29udGluZW50KSkgKyBnZW9tX3BvaW50KCkgKyBmYWNldF93cmFwKH5jb250aW5lbnQpCgpgYGAKClRoZSBgZmFjZXRfZ3JpZGAgZnVuY3Rpb24gd2lsbCBjcmVhdGUgYSBncmlkLWxpa2UgcGxvdCB3aXRoIG9uZSB2YXJpYWJsZSBvbiB0aGUgeC1heGlzIGFuZCBhbm90aGVyIG9uIHRoZSB5LWF4aXMuCgpgYGB7ciBmaWcud2lkdGg9MTJ9CmdncGxvdChnYXBtaW5kZXIsIGFlcyh4ID0gZ2RwUGVyY2FwLCB5PWxpZmVFeHAsY29sPWNvbnRpbmVudCkpICsgZ2VvbV9wb2ludCgpICsgZmFjZXRfd3JhcChjb250aW5lbnR+eWVhcikKYGBgCgojIEludGVyYWN0aW9uIGJldHdlZW4gZ2dwbG90MiBhbmQgZHBseXIKClRoZSBwcmV2aW91cyBwbG90IHdhcyBhIGJpdCBtZXNzeSBhcyBpdCBjb250YWluZWQgYWxsIGNvbWJpbmF0aW9ucyBvZiB5ZWFyIGFuZCBjb250aW5lbnQuIExldCdzIHN1cHBvc2Ugd2Ugd2FudCBvdXIgYW5hbHlzaXMgdG8gYmUgYSBiaXQgbW9yZSBmb2N1c3NlZCBhbmQgZGlzcmVnYXJkIGNvdW50cmllcyBpbiBPY2VhbmlhIChhcyB0aGVyZSBhcmUgb25seSAyIGluIG91ciBkYXRhc2V0KSBhbmQgeWVhcnMgYmV0d2VlbiAxOTk3IGFuZCAyMDAyLiBXZSBzaG91bGQga25vdyBob3cgdG8gcmVzdHJpY3QgdGhlIHJvd3MgZnJvbSB0aGUgYGdhcG1pbmRlcmAgZGF0YXNldCB1c2luZyB0aGUgYGZpbHRlcmAgZnVuY3Rpb24uIEluc3RlYWQgb2YgZmlsdGVyaW5nIHRoZSBkYXRhLCBjcmVhdGluZyBhIG5ldyBkYXRhIGZyYW1lIGFuZCBjb25zdHJ1Y2luZyB0aGUgZGF0YSBmcmFtZSBmcm9tIHRoZXNlIG5ldyBkYXRhIHdlIGNhbiB1c2UgdGhlYCAlPiVgIG9wZXJhdG9yIHRvIGNyZWF0ZSB0aGUgZGF0YSBmcmFtZSBvbiB0aGUgZmx5IGFuZCBwYXNzIGRpcmVjdGx5IHRvIGBnZ3Bsb3RgLiBUaHVzIHdlIGRvbid0IGhhdmUgdG8gc2F2ZSBhIG5ldyBkYXRhIGZyYW1lIG9yIGFsdGVyIHRoZSBvcmlnaW5hbCBkYXRhLgoKCmBgYHtyIGZpZy53aWR0aD0xMn0KZmlsdGVyKGdhcG1pbmRlciwgY29udGluZW50IT0iT2NlYW5pYSIsIHllYXIgJWluJSBjKDE5OTcsMjAwMiwyMDA3KSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGdkcFBlcmNhcCwgeT1saWZlRXhwLGNvbD1jb250aW5lbnQpKSArIGdlb21fcG9pbnQoKSArIGZhY2V0X3dyYXAoY29udGluZW50fnllYXIpCmBgYAoKSW4gYW5vdGhlciBleGFtcGxlIHdlIG1pZ2h0IHdhbnQgdG8gbG9vayBhdCBob3cgbGlmZSBleHBlY3RhbmN5IGhhcyBjaGFuZ2VkIGZvciBwYXJ0aWN1bGFyIGNvdW50cmllcy4KCmBgYHtyfQpmaWx0ZXIoZ2FwbWluZGVyLCBjb3VudHJ5ICVpbiUgYygiVW5pdGVkIEtpbmdkb20iLCAiRnJhbmNlIiwiU3BhaW4iKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IHllYXIsIHk9bGlmZUV4cCxjb2w9Y291bnRyeSkpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9saW5lKCkKYGBgCgoKCiMgU3VtbWFyaXNpbmcgYW5kIGdyb3VwaW5nIHdpdGggZHBseXIKClRoZSBgc3VtbWFyaXNlYCBmdW5jdGlvbiBjYW4gdGFrZSBhbnkgUiBmdW5jdGlvbiB0aGF0IHRha2VzIGEgdmVjdG9yIG9mIHZhbHVlcyAoaS5lLiBhIGNvbHVtbiBmcm9tIGEgZGF0YSBmcmFtZSkgYW5kIHJldHVybnMgYSBzaW5nbGUgdmFsdWUuIFNvbWUgb2YgdGhlIG1vcmUgdXNlZnVsIGZ1bmN0aW9ucyBpbmNsdWRlOgoKLSBgbWluYCBtaW5pbXVtIHZhbHVlCi0gYG1heGAgbWF4aW11bSB2YWx1ZQotIGBzdW1gIHN1bSBvZiB2YWx1ZXMKLSBgbWVhbmAgbWVhbiB2YWx1ZQotIGBzZGAgc3RhbmRhcmQgZGV2aWF0aW9uCi0gYG1lZGlhbmAgbWVkaWFuIHZhbHVlCi0gYElRUmAgdGhlIGludGVycXVhcnRpbGUgcmFuZ2UKLSBgbl9kaXN0aW5jdGAgdGhlIG51bWJlciBvZiBkaXN0aW5jdCB2YWx1ZXMKLSBgbmAgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgKE5vdGU6IHRoaXMgaXMgYSBzcGVjaWFsIGZ1bmN0aW9uIHRoYXQgZG9lc27igJl0IHRha2UgYSB2ZWN0b3IgYXJndW1lbnQsIGkuZS4gY29sdW1uKQoKCmBgYHtyfQpzdW1tYXJpc2UoZ2FwbWluZGVyLCBtaW4obGlmZUV4cCksIG1heChnZHBQZXJjYXApLCBtZWFuKHBvcCkpCmBgYAoKSXQgaXMgYWxzbyBwb3NzaWJsZSB0byBzdW1tYXJpc2UgdXNpbmcgYSBmdW5jdGlvbiB0aGF0IHRha2VzIG1vcmUgdGhhbiBvbmUgdmFsdWUsIGkuZS4gZnJvbSBtdWx0aXBsZSBjb2x1bW5zLiBGb3IgZXhhbXBsZSwgd2UgY291bGQgY29tcHV0ZSB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB5ZWFyIGFuZCBsaWZlIGV4cGVjdGFuY3kuIEhlcmUgd2UgYWxzbyBhc3NpZ24gbmFtZXMgdG8gdGhlIHRhYmxlIHRoYXQgaXMgcHJvZHVjZWQuCgpgYGB7cn0Kc3VtbWFyaXNlKGdhcG1pbmRlciwgTWluTGlmZUV4cGVjdGFuY3kgPSBtaW4obGlmZUV4cCksIAogICAgICAgICAgTWF4aW11bUdEUCA9IG1heChnZHBQZXJjYXApLCAKICAgICAgICAgIEF2ZXJhZ2VQb3A9bWVhbihwb3ApLCAKICAgICAgICAgIENvcnJlbGF0aW9uPWNvcih5ZWFyLGxpZmVFeHApKQpgYGAKCkhvd2V2ZXIsIGl0IGlzIG5vdCBwYXJ0aWN1bGFybHkgdXNlZnVsIHRvIGNhbGN1bGF0ZSBzdWNoIHZhbHVlcyBmcm9tIHRoZSBlbnRpcmUgdGFibGUgYXMgd2UgaGF2ZSBkaWZmZXJlbnQgY29udGluZW50cyBhbmQgeWVhcnMuIFRoZSBgZ3JvdXBfYnlgIGZ1bmN0aW9uIGFsbG93cyB1cyB0byBzcGxpdCB0aGUgdGFibGUgaW50byBkaWZmZXJlbnQgY2F0ZWdvcmllcywgYW5kIGNvbXB1dGUgc3VtbWFyeSBzdGF0aXN0aWNzLiBXZSBjYW4gZ3JvdXAgdGhlIGRhdGEgYWNjb3JkaW5nIHRvIHllYXIgYW5kIGNvbXB1dGUgdGhlIAoKYGBge3J9Cmdyb3VwX2J5KGdhcG1pbmRlciwgeWVhcikgJT4lIHN1bW1hcmlzZSggTWluTGlmZUV4cGVjdGFuY3kgPSBtaW4obGlmZUV4cCksIAogICAgICAgICAgTWF4aW11bUdEUCA9IG1heChnZHBQZXJjYXApLCAKICAgICAgICAgIEF2ZXJhZ2VQb3A9bWVhbihwb3ApKQpgYGAKClRoZSBuaWNlIHRoaW5nIGFib3V0IGBzdW1tYXJpc2VgIGlzIHRoYXQgaXQgY2FuIGZvbGxvd2VkIHVwIGJ5IGFueSBvZiB0aGUgb3RoZXIgYGRwbHlyYCB2ZXJicyB0aGF0IHdlIGhhdmUgbWV0IHNvIGZhciAoYHNlbGVjdGAsIGBmaWx0ZXJgLCBgYXJyYW5nZWAuLmV0YykuIAoKUmV0dXJuaW5nIHRvIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGxpZmUgZXhwZWN0YW5jeSBhbmQgeWVhciwgd2UgY2FuIHN1bW1hcmlzZSBhcyBmb2xsb3dzOi0KCmBgYHtyfQpncm91cF9ieShnYXBtaW5kZXIsIGNvdW50cnkpICU+JSBzdW1tYXJpc2UoQ29ycmVsYXRpb24gPSBjb3IoeWVhciAsbGlmZUV4cCkpCmBgYApXZSBjYW4gdGhlbiBhcnJhbmdlIHRoZSB0YWJsZSBieSB0aGUgY29ycmVsYXRpb24gdG8gc2VlIHdoaWNoIGNvdW50cmllcyBoYXZlIHRoZSBsb3dlc3QgY29ycmVsYXRpb24KCmBgYHtyfQpncm91cF9ieShnYXBtaW5kZXIsIGNvdW50cnkpICU+JSBzdW1tYXJpc2UoQ29ycmVsYXRpb24gPSBjb3IoeWVhciAsbGlmZUV4cCkpICU+JSBhcnJhbmdlKENvcnJlbGF0aW9uKQpgYGAKCldlIGNhbiBmaWx0ZXIgdGhlIHJlc3VsdHMgdG8gZmluZCBvYnNldmF0aW9ucyBvZiBpbnRlcmVzdAoKYGBge3J9Cmdyb3VwX2J5KGdhcG1pbmRlciwgY291bnRyeSkgJT4lIAogIHN1bW1hcmlzZShDb3JyZWxhdGlvbiA9IGNvcih5ZWFyICxsaWZlRXhwKSkgJT4lIAogIGZpbHRlcihDb3JyZWxhdGlvbiA8IDApCmBgYAoKVGhlIGNvdW50cmllcyB3ZSBpZGVudGlmeSBjb3VsZCB0aGVuIGJlIHVzZWQgYXMgdGhlIGJhc2lzIGZvciBhIHBsb3QuCgpgYGB7cn0KZmlsdGVyKGdhcG1pbmRlciwgY291bnRyeSAlaW4lIGMoIlJ3YW5kYSIsIlphbWJpYSIsIlppbWJhYndlIikpICU+JSAKICBnZ3Bsb3QoYWVzKHg9eWVhciwgeT1saWZlRXhwLGNvbD1jb3VudHJ5KSkgKyBnZW9tX2xpbmUoKQpgYGAgCgojIEpvaW5pbmcKCkluIG1hbnkgcmVhbCBsaWZlIHNpdHVhdGlvbnMsIGRhdGEgYXJlIHNwcmVhZCBhY3Jvc3MgbXVsdGlwbGUgdGFibGVzIG9yIHNwcmVhZHNoZWV0cy4gVXN1YWxseSB0aGlzIG9jY3VycyBiZWNhdXNlIGRpZmZlcmVudCB0eXBlcyBvZiBpbmZvcm1hdGlvbiBhYm91dCBhIHN1YmplY3QsIGUuZy4gYSBwYXRpZW50LCBhcmUgY29sbGVjdGVkIGZyb20gZGlmZmVyZW50IHNvdXJjZXMuIEl0IG1heSBiZSBkZXNpcmFibGUgZm9yIHNvbWUgYW5hbHlzZXMgdG8gY29tYmluZSBkYXRhIGZyb20gdHdvIG9yIG1vcmUgdGFibGVzIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZSBiYXNlZCBvbiBhIGNvbW1vbiBjb2x1bW4sIGZvciBleGFtcGxlLCBhbiBhdHRyaWJ1dGUgdGhhdCB1bmlxdWVseSBpZGVudGlmaWVzIHRoZSBzdWJqZWN0LgoKYGRwbHlyYCBwcm92aWRlcyBhIHNldCBvZiBqb2luIGZ1bmN0aW9ucyBmb3IgY29tYmluaW5nIHR3byBkYXRhIGZyYW1lcyBiYXNlZCBvbiBtYXRjaGVzIHdpdGhpbiBzcGVjaWZpZWQgY29sdW1ucy4gRm9yIHRob3NlIGZhbWlsaWFyIHdpdGggc3VjaCBTUUwsIHRoZXNlIG9wZXJhdGlvbnMgYXJlIHZlcnkgc2ltaWxhciB0byBjYXJyeWluZyBvdXQgam9pbiBvcGVyYXRpb25zIGJldHdlZW4gdGFibGVzIGluIGEgcmVsYXRpb25hbCBkYXRhYmFzZS4KCkFzIGEgdG95IGV4YW1wbGUsIGxldHMgY29uc2lkZXIgdHdvIGRhdGEgZnJhbWVzIHRoYXQgY29udGFpbiB0aGUgbmFtZXMgb2YgdmFyaW91cyBiYW5kcywgYW5kIHRoZSBpbnN0cnVtZW50cyB0aGF0IHRoZXkgcGxheTotCmBgYHtyfQpiYW5kX2luc3RydW1lbnRzCmJhbmRfbWVtYmVycwpgYGAKClRoZXJlIGFyZSB2YXJpb3VzIHdheXMgaW4gd2hpY2ggd2UgY2FuIGpvaW4gdGhlc2UgdHdvIHRhYmxlcyB0b2dldGhlci4gV2Ugd2lsbCBqdXN0IGNvbnNpZGVyIHRoZSBjYXNlIG9mIGEgImxlZnQgam9pbiIuCgohW10oaW1hZ2VzL2xlZnQtam9pbi5naWYpCgoqQW5pbWF0ZWQgZ2lmIGJ5IEdhcnJpY2sgQWRlbi1CdWllKgoKYGxlZnRfam9pbmAgcmV0dXJucyBhbGwgcm93cyBmcm9tIHRoZSBmaXJzdCBkYXRhIGZyYW1lIHJlZ2FyZGxlc3Mgb2Ygd2hldGhlciB0aGVyZSBpcyBhIG1hdGNoIGluIHRoZSBzZWNvbmQgZGF0YSBmcmFtZS4gUm93cyB3aXRoIG5vIG1hdGNoIGFyZSBpbmNsdWRlZCBpbiB0aGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgYnV0IGhhdmUgTkEgdmFsdWVzIGluIHRoZSBhZGRpdGlvbmFsIGNvbHVtbnMgY29taW5nIGZyb20gdGhlIHNlY29uZCBkYXRhIGZyYW1lLgoKQW5pbWF0aW9ucyB0byBpbGx1c3RyYXRlIG90aGVyIHR5cGVzIG9mIGpvaW4gYXJlIGF2YWlsYWJsZSBhdCBbaHR0cHM6Ly9naXRodWIuY29tL2dhZGVuYnVpZS90aWR5LWFuaW1hdGVkLXZlcmJzXShodHRwczovL2dpdGh1Yi5jb20vZ2FkZW5idWllL3RpZHktYW5pbWF0ZWQtdmVyYnMpCgpgYGB7cn0KbGVmdF9qb2luKGJhbmRfbWVtYmVycyxiYW5kX2luc3RydW1lbnRzKQpgYGAKCmByaWdodF9qb2luYCBpcyBzaW1pbGFyIGJ1dCByZXR1cm5zIGFsbCByb3dzIGZyb20gdGhlIHNlY29uZCBkYXRhIGZyYW1lIHRoYXQgaGF2ZSBhIG1hdGNoIHdpdGggcm93cyBpbiB0aGUgZmlyc3QgZGF0YSBmcmFtZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIGNvbHVtbi4KCmBgYHtyfQpyaWdodF9qb2luKGJhbmRfbWVtYmVycyxiYW5kX2luc3RydW1lbnRzKQpgYGAKCmBpbm5lcl9qb2luYCBvbmx5IHJldHVybnMgdGhvc2Ugcm93cyB3aGVyZSBtYXRjaGVzIGNvdWxkIGJlIG1hZGUKCmBgYHtyfQppbm5lcl9qb2luKGJhbmRfbWVtYmVycyxiYW5kX2luc3RydW1lbnRzKQpgYGAKKioqKioqCioqKioqKgoqKioqKioKCiMjIyBFeGVyY2lzZSAob3Blbi1lbmRlZCkKCi0gVGhlIGZpbGUgYG1lZGFsX3RhYmxlLmNzdmAgaW4gdGhlIGByYXdfZGF0YS9gIHByb2plY3Qgc3ViLWRpcmVjdG9yeSBjb250YWlucyBkYXRhIGFib3V0IGhvdyBtYW55IG1lZGFscyBob3cgYmVlbiB3b24gYnkgdmFyaW91cyBjb3VudHJpZXMgYXQgdGhlIEJlaWppbmcgc3VtbWVyIG9seW1waWNzIG9mIDIwMDguCi0gUmVhZCB0aGlzIGNzdiBmaWxlIGludG8gUiBhbmQgam9pbiB3aXRoIHRoZSBgZ2FwbWluZGVyYCBkYXRhIGZyb20gMjAwNwotIFdoYXQgaW50ZXJlc3Rpbmcgc3VtbWFyaWVzIC8gcGxvdHMgY2FuIHlvdSBtYWtlIGZyb20gdGhlIGRhdGE/CgoqKioqKioKKioqKioqCioqKioqKgoKCgoK